diff options
967 files changed, 22949 insertions, 9329 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..628894016f39 100644 --- a/Android.bp +++ b/Android.bp @@ -69,7 +69,6 @@ java_defaults { "core/java/android/app/ITaskStackListener.aidl", "core/java/android/app/IBackupAgent.aidl", "core/java/android/app/IEphemeralResolver.aidl", - "core/java/android/app/IInputForwarder.aidl", "core/java/android/app/IInstantAppResolver.aidl", "core/java/android/app/IInstrumentationWatcher.aidl", "core/java/android/app/INotificationManager.aidl", @@ -209,6 +208,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 +772,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 +893,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 +913,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 +1195,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/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp new file mode 100644 index 000000000000..65c28fb4d0a7 --- /dev/null +++ b/apct-tests/perftests/autofill/Android.bp @@ -0,0 +1,26 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "AutofillPerfTests", + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + static_libs: [ + "androidx.test.rules", + "androidx.annotation_annotation", + "apct-perftests-utils", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/apct-tests/perftests/autofill/Android.mk b/apct-tests/perftests/autofill/Android.mk deleted file mode 100644 index f4da40b3175a..000000000000 --- a/apct-tests/perftests/autofill/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.rules \ - androidx.annotation_annotation \ - apct-perftests-utils - -LOCAL_PACKAGE_NAME := AutofillPerfTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_COMPATIBILITY_SUITE += device-tests - -include $(BUILD_PACKAGE) diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp new file mode 100644 index 000000000000..508bf6007a9f --- /dev/null +++ b/apct-tests/perftests/multiuser/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "MultiUserPerfTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "apct-perftests-utils", + ], + platform_apis: true, + test_suites: ["device-tests"], + certificate: "platform", +} diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk deleted file mode 100644 index 5852044effa8..000000000000 --- a/apct-tests/perftests/multiuser/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.rules \ - apct-perftests-utils - -LOCAL_PACKAGE_NAME := MultiUserPerfTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_COMPATIBILITY_SUITE += device-tests - -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) - diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp new file mode 100644 index 000000000000..be85816629c1 --- /dev/null +++ b/apct-tests/perftests/utils/Android.bp @@ -0,0 +1,9 @@ +java_library { + name: "apct-perftests-utils", + static_libs: [ + "androidx.test.rules", + "androidx.annotation_annotation", + ], + // Build all java files in the java subdirectory + srcs: ["**/*.java"], +} diff --git a/apct-tests/perftests/utils/Android.mk b/apct-tests/perftests/utils/Android.mk deleted file mode 100644 index 19f83c8f5bb0..000000000000 --- a/apct-tests/perftests/utils/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.test.rules \ - androidx.annotation_annotation - -# Build all java files in the java subdirectory -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -# The name of the jar file to create -LOCAL_MODULE := apct-perftests-utils - -# Build a static jar file. -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING index 8a676e994081..2cdf54bbad6f 100644 --- a/api/TEST_MAPPING +++ b/api/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "CtsCurrentApiSignatureTestCases" + }, + { + "name": "GtsUnofficialApisUsageTestCases" } ] } diff --git a/api/current.txt b/api/current.txt index 0424a725e555..cb79f5a80ed8 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 @@ -9346,25 +9348,7 @@ package android.content { field public static final android.os.Parcelable.Creator<android.content.ComponentName> CREATOR; } - public interface ContentInterface { - method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException; - method public int bulkInsert(@NonNull android.net.Uri, @NonNull android.content.ContentValues[]) throws android.os.RemoteException; - method @Nullable public android.os.Bundle call(@NonNull String, @NonNull String, @Nullable String, @Nullable android.os.Bundle) throws android.os.RemoteException; - method @Nullable public android.net.Uri canonicalize(@NonNull android.net.Uri) throws android.os.RemoteException; - method public int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]) throws android.os.RemoteException; - method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String) throws android.os.RemoteException; - method @Nullable public String getType(@NonNull android.net.Uri) throws android.os.RemoteException; - method @Nullable public android.net.Uri insert(@NonNull android.net.Uri, @Nullable android.content.ContentValues) throws android.os.RemoteException; - method @Nullable public android.content.res.AssetFileDescriptor openAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException; - method @Nullable public android.os.ParcelFileDescriptor openFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException; - method @Nullable public android.content.res.AssetFileDescriptor openTypedAssetFile(@NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException; - method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws android.os.RemoteException; - method public boolean refresh(@NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws android.os.RemoteException; - method @Nullable public android.net.Uri uncanonicalize(@NonNull android.net.Uri) throws android.os.RemoteException; - method public int update(@NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]) throws android.os.RemoteException; - } - - public abstract class ContentProvider implements android.content.ComponentCallbacks2 android.content.ContentInterface { + public abstract class ContentProvider implements android.content.ComponentCallbacks2 { ctor public ContentProvider(); method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException; method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException; @@ -9417,7 +9401,7 @@ package android.content { method public void writeDataToPipe(@NonNull android.os.ParcelFileDescriptor, @NonNull android.net.Uri, @NonNull String, @Nullable android.os.Bundle, @Nullable T); } - public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface { + public class ContentProviderClient implements java.lang.AutoCloseable { method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException; method @NonNull public android.content.ContentProviderResult[] applyBatch(@NonNull String, @NonNull java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException; method public int bulkInsert(@NonNull android.net.Uri, @NonNull android.content.ContentValues[]) throws android.os.RemoteException; @@ -9425,7 +9409,6 @@ package android.content { method @Nullable public android.os.Bundle call(@NonNull String, @NonNull String, @Nullable String, @Nullable android.os.Bundle) throws android.os.RemoteException; method @Nullable public final android.net.Uri canonicalize(@NonNull android.net.Uri) throws android.os.RemoteException; method public void close(); - method public static void closeQuietly(android.content.ContentProviderClient); method public int delete(@NonNull android.net.Uri, @Nullable String, @Nullable String[]) throws android.os.RemoteException; method @Nullable public android.content.ContentProvider getLocalContentProvider(); method @Nullable public String[] getStreamTypes(@NonNull android.net.Uri, @NonNull String) throws android.os.RemoteException; @@ -9500,8 +9483,8 @@ package android.content { method public void setKeepUpdated(boolean); } - public abstract class ContentResolver implements android.content.ContentInterface { - ctor public ContentResolver(android.content.Context); + public abstract class ContentResolver { + ctor public ContentResolver(@Nullable android.content.Context); method @Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull android.net.Uri); method @Nullable public final android.content.ContentProviderClient acquireContentProviderClient(@NonNull String); method @Nullable public final android.content.ContentProviderClient acquireUnstableContentProviderClient(@NonNull android.net.Uri); @@ -9566,6 +9549,8 @@ package android.content { method public final void unregisterContentObserver(@NonNull android.database.ContentObserver); method public final int update(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable android.content.ContentValues, @Nullable String, @Nullable String[]); method public static void validateSyncExtrasBundle(android.os.Bundle); + method public static android.content.ContentResolver wrap(@NonNull android.content.ContentProvider); + method public static android.content.ContentResolver wrap(@NonNull android.content.ContentProviderClient); field public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*"; field public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir"; field public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item"; @@ -9607,10 +9592,10 @@ package android.content { public class ContentUris { ctor public ContentUris(); - method public static android.net.Uri.Builder appendId(android.net.Uri.Builder, long); - method public static long parseId(android.net.Uri); - method public static android.net.Uri removeId(android.net.Uri); - method public static android.net.Uri withAppendedId(android.net.Uri, long); + method @NonNull public static android.net.Uri.Builder appendId(@NonNull android.net.Uri.Builder, long); + method public static long parseId(@NonNull android.net.Uri); + method @NonNull public static android.net.Uri removeId(@NonNull android.net.Uri); + method @NonNull public static android.net.Uri withAppendedId(@NonNull android.net.Uri, long); } public final class ContentValues implements android.os.Parcelable { @@ -10237,8 +10222,7 @@ package android.content { field public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS"; field public static final String ACTION_MEDIA_REMOVED = "android.intent.action.MEDIA_REMOVED"; field public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED"; - field public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE"; - field public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME"; + field @Deprecated public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE"; field public static final String ACTION_MEDIA_SCANNER_STARTED = "android.intent.action.MEDIA_SCANNER_STARTED"; field public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED"; field public static final String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE"; @@ -13064,6 +13048,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 +14084,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 +23396,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 +23431,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); @@ -25972,6 +25990,7 @@ package android.media { method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle); method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object); method public void close(); + method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers(); method @NonNull public String getSessionId(); method @NonNull public android.media.Session2Token getSessionToken(); method public boolean isPlaybackActive(); @@ -25998,6 +26017,7 @@ package android.media { method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result); method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); + method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle); } @@ -26064,6 +26084,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 +27474,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 +27525,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(); @@ -29907,7 +29938,7 @@ package android.net.wifi { method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); - method public boolean isDeviceToApRttSupported(); + method @Deprecated public boolean isDeviceToApRttSupported(); method public boolean isEasyConnectSupported(); method public boolean isEnhancedPowerReportingSupported(); method public boolean isOweSupported(); @@ -34575,11 +34606,9 @@ package android.os { ctor public FileUriExposedException(String); } - public class FileUtils { + public final class FileUtils { method public static void closeQuietly(@Nullable AutoCloseable); method public static void closeQuietly(@Nullable java.io.FileDescriptor); - method public static long copy(@NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException; - method public static long copy(@NonNull java.io.File, @NonNull java.io.File, @Nullable android.os.CancellationSignal, @Nullable java.util.concurrent.Executor, @Nullable android.os.FileUtils.ProgressListener) throws java.io.IOException; method public static long copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws java.io.IOException; method public static long copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream, @Nullable android.os.CancellationSignal, @Nullable java.util.concurrent.Executor, @Nullable android.os.FileUtils.ProgressListener) throws java.io.IOException; method public static long copy(@NonNull java.io.FileDescriptor, @NonNull java.io.FileDescriptor) throws java.io.IOException; @@ -35050,7 +35079,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"; @@ -35651,6 +35680,7 @@ package android.os.storage { method public String getMountedObbPath(String); method @NonNull public android.os.storage.StorageVolume getPrimaryStorageVolume(); method @Nullable public android.os.storage.StorageVolume getStorageVolume(java.io.File); + method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri); method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes(); method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException; method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor); @@ -38123,38 +38153,26 @@ package android.provider { method public static android.net.Uri buildRootsUri(String); method public static android.net.Uri buildSearchDocumentsUri(String, String, String); method public static android.net.Uri buildTreeDocumentUri(String, String); - method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, String, String) throws java.io.FileNotFoundException; - method @Deprecated public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, String, String) throws java.io.FileNotFoundException; - method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException; - method @Deprecated public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException; - method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; - method public static void ejectRoot(android.content.ContentInterface, android.net.Uri); - method @Deprecated public static void ejectRoot(android.content.ContentResolver, android.net.Uri); - method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; + method @Nullable public static android.net.Uri copyDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException; + method @Nullable public static android.net.Uri createDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull String, @NonNull String) throws java.io.FileNotFoundException; + method @Nullable public static android.content.IntentSender createWebLinkIntent(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @Nullable android.os.Bundle) throws java.io.FileNotFoundException; + method public static boolean deleteDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException; + method public static void ejectRoot(@NonNull android.content.ContentResolver, @NonNull android.net.Uri); + method @Nullable public static android.provider.DocumentsContract.Path findDocumentPath(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException; method public static String getDocumentId(android.net.Uri); - method @Nullable public static android.os.Bundle getDocumentMetadata(@NonNull android.content.ContentInterface, @NonNull android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; - method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException; - method @Deprecated public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException; + method @Nullable public static android.os.Bundle getDocumentMetadata(@NonNull android.content.ContentResolver, @NonNull android.net.Uri) throws java.io.FileNotFoundException; + method @Nullable public static android.graphics.Bitmap getDocumentThumbnail(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.graphics.Point, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException; method public static String getRootId(android.net.Uri); method public static String getSearchDocumentsQuery(android.net.Uri); method public static String getTreeDocumentId(android.net.Uri); - method public static boolean isChildDocument(@NonNull android.content.ContentInterface, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; + method public static boolean isChildDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException; method public static boolean isDocumentUri(android.content.Context, @Nullable android.net.Uri); method public static boolean isRootUri(@NonNull android.content.Context, @Nullable android.net.Uri); method public static boolean isRootsUri(@NonNull android.content.Context, @Nullable android.net.Uri); method public static boolean isTreeUri(android.net.Uri); - method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method @Deprecated public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException; - method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, String) throws java.io.FileNotFoundException; - method @Deprecated public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, String) throws java.io.FileNotFoundException; + method @Nullable public static android.net.Uri moveDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException; + method public static boolean removeDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException; + method @Nullable public static android.net.Uri renameDocument(@NonNull android.content.ContentResolver, @NonNull android.net.Uri, @NonNull String) throws java.io.FileNotFoundException; field public static final String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS"; field public static final String EXTRA_ERROR = "error"; field public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF"; @@ -38589,13 +38607,13 @@ package android.provider { public static final class MediaStore.Images.Media implements android.provider.MediaStore.Images.ImageColumns { ctor public MediaStore.Images.Media(); - method public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException; + method @Deprecated public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException; method public static android.net.Uri getContentUri(String); - method public static String insertImage(android.content.ContentResolver, String, String, String) throws java.io.FileNotFoundException; - method public static String insertImage(android.content.ContentResolver, android.graphics.Bitmap, String, String); - method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]); - method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String); - method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String[], String); + method @Deprecated public static String insertImage(android.content.ContentResolver, String, String, String) throws java.io.FileNotFoundException; + method @Deprecated public static String insertImage(android.content.ContentResolver, android.graphics.Bitmap, String, String); + method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]); + method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String); + method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[], String, String[], String); field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image"; field public static final String DEFAULT_SORT_ORDER = "bucket_display_name"; field public static final android.net.Uri EXTERNAL_CONTENT_URI; @@ -38664,7 +38682,7 @@ package android.provider { public static final class MediaStore.Video { ctor public MediaStore.Video(); - method public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]); + method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]); field public static final String DEFAULT_SORT_ORDER = "_display_name"; } @@ -41547,17 +41565,18 @@ package android.service.media { package android.service.notification { public final class Adjustment implements android.os.Parcelable { - ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int); + ctor public Adjustment(String, String, android.os.Bundle, CharSequence, android.os.UserHandle); method public int describeContents(); method public CharSequence getExplanation(); method public String getKey(); method public String getPackage(); method public android.os.Bundle getSignals(); - method public int getUser(); + method public android.os.UserHandle getUserHandle(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; field public static final String KEY_IMPORTANCE = "key_importance"; + field public static final String KEY_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 +41634,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 +41974,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 +41983,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"; @@ -44395,6 +44414,7 @@ package android.telephony { method public String getMccString(); method public String getMncString(); method @Nullable public String getMobileNetworkOperator(); + method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR; } @@ -45069,12 +45089,12 @@ package android.telephony { method public boolean canChangeDtmfToneLength(); method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); - method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); method public int getCallState(); method public int getCardIdForDefaultEuicc(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig(); method public int getCarrierIdFromSimMccMnc(); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int); method public int getDataActivity(); @@ -45104,7 +45124,7 @@ package android.telephony { method public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState(); + method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(); method @Nullable public android.telephony.SignalStrength getSignalStrength(); method public int getSimCarrierId(); method @Nullable public CharSequence getSimCarrierIdName(); @@ -45146,8 +45166,8 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method public void listen(android.telephony.PhoneStateListener, int); - method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); + method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); method public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); @@ -45216,7 +45236,6 @@ package android.telephony { field public static final String EXTRA_STATE_RINGING; field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID"; field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER"; - field public static final int INVALID_CARD_ID = -1; // 0xffffffff field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"; field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7 field public static final int NETWORK_TYPE_CDMA = 4; // 0x4 @@ -45255,7 +45274,9 @@ package android.telephony { field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3 field public static final int SIM_STATE_READY = 5; // 0x5 field public static final int SIM_STATE_UNKNOWN = 0; // 0x0 + field public static final int UNINITIALIZED_CARD_ID = -2; // 0xfffffffe field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff + field public static final int UNSUPPORTED_CARD_ID = -1; // 0xffffffff field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff field public static final String VVM_TYPE_CVVM = "vvm_type_cvvm"; @@ -48997,6 +49018,7 @@ package android.util { } public final class StatsLog { + method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public static boolean logBinaryPushStateChanged(@NonNull String, long, int, int, @NonNull long[]); method public static boolean logEvent(int); method public static boolean logStart(int); method public static boolean logStop(int); @@ -49216,6 +49238,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 { @@ -50804,7 +50827,7 @@ package android.view { method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getAlpha(); method public android.view.animation.Animation getAnimation(); method public android.os.IBinder getApplicationWindowToken(); - method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(); + method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(@AttrRes int); method @NonNull public java.util.Map<java.lang.Integer,java.lang.Integer> getAttributeSourceResourceMap(); method @android.view.ViewDebug.ExportedProperty @Nullable public String[] getAutofillHints(); method public final android.view.autofill.AutofillId getAutofillId(); @@ -50854,6 +50877,8 @@ package android.view { method public void getHitRect(android.graphics.Rect); method public int getHorizontalFadingEdgeLength(); method protected int getHorizontalScrollbarHeight(); + method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarThumbDrawable(); + method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarTrackDrawable(); method @android.view.ViewDebug.CapturedViewProperty @IdRes public int getId(); method @android.view.ViewDebug.ExportedProperty(category="accessibility", mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to="noHideDescendants")}) public int getImportantForAccessibility(); method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForAutofill(); @@ -50944,6 +50969,8 @@ package android.view { method public long getUniqueDrawingId(); method public int getVerticalFadingEdgeLength(); method public int getVerticalScrollbarPosition(); + method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable(); + method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable(); method public int getVerticalScrollbarWidth(); method public android.view.ViewTreeObserver getViewTreeObserver(); method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.VISIBLE, to="VISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.INVISIBLE, to="INVISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.GONE, to="GONE")}) public int getVisibility(); @@ -51187,6 +51214,8 @@ package android.view { method public void setHasTransientState(boolean); method public void setHorizontalFadingEdgeEnabled(boolean); method public void setHorizontalScrollBarEnabled(boolean); + method public void setHorizontalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable); + method public void setHorizontalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable); method public void setHovered(boolean); method public void setId(@IdRes int); method public void setImportantForAccessibility(int); @@ -51276,6 +51305,8 @@ package android.view { method public void setVerticalFadingEdgeEnabled(boolean); method public void setVerticalScrollBarEnabled(boolean); method public void setVerticalScrollbarPosition(int); + method public void setVerticalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable); + method public void setVerticalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable); method public void setVisibility(int); method @Deprecated public void setWillNotCacheDrawing(boolean); method public void setWillNotDraw(boolean); @@ -53463,6 +53494,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); @@ -53470,6 +53502,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 { @@ -55734,6 +55767,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(); @@ -55758,6 +55792,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/removed.txt b/api/removed.txt index f5bd434c3cc0..c4ed871d0661 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -558,7 +558,7 @@ package android.telephony { public class TelephonyManager { method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); - method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); + method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); } } diff --git a/api/system-current.txt b/api/system-current.txt index 48a450a441a9..0239660db6f8 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); } @@ -302,16 +304,18 @@ package android.app { } public class AppOpsManager { - method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); + method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); method public static String[] getOpStrs(); 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"; @@ -390,6 +394,17 @@ package android.app { field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR; } + public static final class AppOpsManager.HistoricalOpsRequest { + } + + public static final class AppOpsManager.HistoricalOpsRequest.Builder { + ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int); + } + public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); @@ -439,6 +454,7 @@ package android.app { public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); + method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setAllowBackgroundActivityStarts(boolean); method public void setDontSendToRestrictedApps(boolean); method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); @@ -545,15 +561,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 { @@ -1278,14 +1296,14 @@ package android.bluetooth.le { package android.content { - public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface { + public class ContentProviderClient implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); } - public abstract class ContentResolver implements android.content.ContentInterface { - method public android.os.Bundle getCache(android.net.Uri); + public abstract class ContentResolver { + method @Nullable public android.os.Bundle getCache(@NonNull android.net.Uri); method public android.graphics.drawable.Drawable getTypeDrawable(String); - method public void putCache(android.net.Uri, android.os.Bundle); + method public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle); } public abstract class Context { @@ -1366,6 +1384,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 +1395,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"; @@ -1580,7 +1600,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int); - method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], @android.content.pm.PackageManager.DistractionRestriction int); + method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int); method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo); @@ -1658,9 +1678,6 @@ package android.content.pm { method public abstract void onDexModuleRegistered(String, boolean, String); } - @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.content.pm.PackageManager.RESTRICTION_NONE, android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.DistractionRestriction { - } - public static interface PackageManager.OnPermissionsChangedListener { method public void onPermissionsChanged(int); } @@ -3405,13 +3422,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; } @@ -3471,7 +3488,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; } @@ -3480,6 +3497,18 @@ package android.media { method public android.media.AudioRecord.Builder setSessionId(int) throws java.lang.IllegalArgumentException; } + public class HwAudioSource { + method public void start(); + method public void stop(); + } + + public static class HwAudioSource.Builder { + ctor public HwAudioSource.Builder(); + method @NonNull public android.media.HwAudioSource build(); + method @NonNull public android.media.HwAudioSource.Builder setAudioAttributes(@NonNull android.media.AudioAttributes); + method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo); + } + public final class MediaRecorder.AudioSource { field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf @@ -3600,6 +3629,28 @@ package android.media.audiopolicy { method public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException; } + public final class AudioProductStrategies implements java.lang.Iterable<android.media.audiopolicy.AudioProductStrategy> android.os.Parcelable { + ctor public AudioProductStrategies(); + method public int describeContents(); + method @NonNull public android.media.AudioAttributes getAudioAttributesForLegacyStreamType(int); + method @NonNull public android.media.AudioAttributes getAudioAttributesForProductStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); + method @Nullable public android.media.audiopolicy.AudioProductStrategy getById(int); + method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes); + method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator(); + method public int size(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategies> CREATOR; + } + + public final class AudioProductStrategy implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.media.AudioAttributes getAudioAttributes(); + method public int getId(); + method @NonNull public String name(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR; + } + } package android.media.session { @@ -3929,7 +3980,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); @@ -4626,7 +4677,7 @@ package android.net.wifi { method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState(); - method public boolean isDeviceToDeviceRttSupported(); + method @Deprecated public boolean isDeviceToDeviceRttSupported(); method public boolean isPortableHotspotSupported(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); @@ -5305,6 +5356,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); @@ -5319,6 +5374,7 @@ package android.os { public final class PowerManager { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveMode(); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig); @@ -5783,8 +5839,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 { @@ -5946,6 +6004,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"; } @@ -6264,6 +6323,8 @@ package android.service.autofill.augmented { ctor public AugmentedAutofillService(); method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]); + method public void onConnected(); + method public void onDisconnected(); method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback); field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService"; } @@ -6301,20 +6362,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); } } @@ -6340,6 +6392,7 @@ package android.service.contentcapture { public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); + method public final void disableContentCaptureServices(); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); @@ -6348,7 +6401,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"; } @@ -6519,6 +6573,17 @@ package android.service.euicc { package android.service.notification { + public final class Adjustment implements android.os.Parcelable { + ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int); + ctor protected Adjustment(android.os.Parcel); + method public int getUser(); + 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); @@ -6974,6 +7039,7 @@ package android.telecom { field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT"; field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT"; field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE"; + field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL"; field public static final int TTY_MODE_FULL = 1; // 0x1 field public static final int TTY_MODE_HCO = 2; // 0x2 field public static final int TTY_MODE_OFF = 0; // 0x0 @@ -7860,7 +7926,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); - method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); + method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig(); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); @@ -8248,6 +8314,7 @@ package android.telephony.ims { method public int getServiceType(); method public static int getVideoStateFromCallType(int); method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile); + method public boolean hasKnownUserIntentEmergency(); method public boolean isEmergencyCallTesting(); method public boolean isVideoCall(); method public boolean isVideoPaused(); @@ -8260,6 +8327,7 @@ package android.telephony.ims { method public void setEmergencyCallTesting(boolean); method public void setEmergencyServiceCategories(int); method public void setEmergencyUrns(java.util.List<java.lang.String>); + method public void setHasKnownUserIntentEmergency(boolean); method public void updateCallExtras(android.telephony.ims.ImsCallProfile); method public void updateCallType(android.telephony.ims.ImsCallProfile); method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); @@ -9302,7 +9370,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 +9393,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 +9402,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 @@ -9341,7 +9412,6 @@ package android.view.contentcapture { public final class ContentCaptureManager { method public boolean isContentCaptureFeatureEnabled(); - method public void setContentCaptureFeatureEnabled(boolean); } public final class ViewNode extends android.app.assist.AssistStructure.ViewNode { diff --git a/api/test-current.txt b/api/test-current.txt index 1a7e4cb83c52..1ed85625c5a1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -101,8 +101,8 @@ package android.app { public class AppOpsManager { method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory(); - method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(int, @Nullable String, @Nullable String[], long, long, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); - method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(int, @Nullable String, @Nullable String[], long, long, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); + method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); + method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); method public static int getNumOps(); method public static String[] getOpStrs(); method public boolean isOperationActive(int, int, String); @@ -206,6 +206,17 @@ package android.app { field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR; } + public static final class AppOpsManager.HistoricalOpsRequest { + } + + public static final class AppOpsManager.HistoricalOpsRequest.Builder { + ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); + method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int); + } + public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); @@ -472,11 +483,11 @@ package android.bluetooth { package android.content { - public class ContentProviderClient implements java.lang.AutoCloseable android.content.ContentInterface { + public class ContentProviderClient implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); } - public abstract class ContentResolver implements android.content.ContentInterface { + public abstract class ContentResolver { method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int); } @@ -516,13 +527,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 +820,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 +955,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 +1015,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 { @@ -1281,7 +1301,7 @@ package android.os { method public static java.io.File getStorageDirectory(); } - public class FileUtils { + public final class FileUtils { method public static boolean contains(java.io.File, java.io.File); } @@ -1815,6 +1835,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 } @@ -2036,6 +2057,8 @@ package android.service.autofill.augmented { ctor public AugmentedAutofillService(); method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]); + method public void onConnected(); + method public void onDisconnected(); method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback); field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService"; } @@ -2073,20 +2096,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); } } @@ -2102,6 +2116,7 @@ package android.service.contentcapture { public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); + method public final void disableContentCaptureServices(); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); @@ -2110,7 +2125,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 +2715,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 +2742,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 +2751,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 @@ -2742,8 +2761,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/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl index ea7274f3ea58..4a66715ad2b8 100644 --- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl @@ -24,6 +24,7 @@ interface IIdmap2 { const int POLICY_SYSTEM_PARTITION = 0x00000002; const int POLICY_VENDOR_PARTITION = 0x00000004; const int POLICY_PRODUCT_PARTITION = 0x00000008; + const int POLICY_SIGNATURE = 0x00000010; @utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId); boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId); diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 99b5f0ff3c2d..ec498ffb393c 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -284,7 +284,7 @@ std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream, bool CheckOverlayable(const LoadedPackage& target_package, const utils::OverlayManifestInfo& overlay_info, - const PolicyBitmask& fulfilled_polices, const ResourceId& resid) { + const PolicyBitmask& fulfilled_policies, const ResourceId& resid) { const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid); if (overlayable_info == nullptr) { // If the resource does not have an overlayable definition, allow the resource to be overlaid. @@ -299,7 +299,7 @@ bool CheckOverlayable(const LoadedPackage& target_package, } // Enforce policy restrictions if the resource is declared as overlayable. - return (overlayable_info->policy_flags & fulfilled_polices) != 0; + return (overlayable_info->policy_flags & fulfilled_policies) != 0; } std::unique_ptr<const Idmap> Idmap::FromApkAssets( diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp index 0f87ef0c4ea9..6649288dfa41 100644 --- a/cmds/idmap2/libidmap2/Policies.cpp +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -35,6 +35,7 @@ const std::map<android::StringPiece, PolicyFlags> kStringToFlag = { {"product", PolicyFlags::POLICY_PRODUCT_PARTITION}, {"system", PolicyFlags::POLICY_SYSTEM_PARTITION}, {"vendor", PolicyFlags::POLICY_VENDOR_PARTITION}, + {"signature", PolicyFlags::POLICY_SIGNATURE}, }; } // namespace diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 0e0e25f4f0f3..9a0412efbde4 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -129,28 +129,31 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/policy_system_vendor ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/str1 + success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/policy_signature + ASSERT_FALSE(success); + + success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str1 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0000); - success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str2 + success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str2 ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str3 + success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str3 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0001); - success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str4 + success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str4 ASSERT_TRUE(success); ASSERT_EQ(entry, 0x0002); - success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/x + success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/x ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/y + success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/y ASSERT_FALSE(success); - success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/z + success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/z ASSERT_FALSE(success); } diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 8514e12e8c17..2e85eb6215d1 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -39,12 +39,13 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) { [](unsigned char type ATTRIBUTE_UNUSED, const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 6U); + ASSERT_EQ(v->size(), 7U); ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({ root + "/.", root + "/..", root + "/overlay", root + "/target", + root + "/signature-overlay", root + "/system-overlay", root + "/system-overlay-invalid", })); @@ -56,15 +57,22 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0; }); ASSERT_THAT(v, NotNull()); - ASSERT_EQ(v->size(), 9U); + ASSERT_EQ(v->size(), 10U); ASSERT_EQ( std::set<std::string>(v->begin(), v->end()), std::set<std::string>( - {root + "/target/target.apk", root + "/target/target-no-overlayable.apk", - root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk", - root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-static-1.apk", - root + "/overlay/overlay-static-2.apk", root + "/system-overlay/system-overlay.apk", - root + "/system-overlay-invalid/system-overlay-invalid.apk"})); + { + root + "/target/target.apk", + root + "/target/target-no-overlayable.apk", + root + "/overlay/overlay.apk", + root + "/overlay/overlay-no-name.apk", + root + "/overlay/overlay-no-name-static.apk", + root + "/overlay/overlay-static-1.apk", + root + "/overlay/overlay-static-2.apk", + root + "/signature-overlay/signature-overlay.apk", + root + "/system-overlay/system-overlay.apk", + root + "/system-overlay-invalid/system-overlay-invalid.apk" + })); } TEST(FileUtilsTests, ReadFile) { diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 1216f9ec736a..a6a2ada76712 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -132,9 +132,9 @@ TEST_F(Idmap2BinaryTests, Dump) { ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f020009 -> 0x7f020000 string/str1"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020001 string/str3"), std::string::npos); - ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020002 string/str4"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020000 string/str1"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020001 string/str3"), std::string::npos); + ASSERT_NE(result->stdout.find("0x7f02000d -> 0x7f020002 string/str4"), std::string::npos); ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos); // clang-format off @@ -286,7 +286,7 @@ TEST_F(Idmap2BinaryTests, Lookup) { "lookup", "--idmap-path", GetIdmapPath(), "--config", "", - "--resid", "0x7f020009"}); // string/str1 + "--resid", "0x7f02000a"}); // string/str1 // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index b40521f054af..53ec03ba3d5e 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -191,8 +191,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); - ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xdd53ca29); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xa71ccd77); + ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xd513ca1b); + ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed); ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path); @@ -217,7 +217,7 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 9U); + ASSERT_EQ(types[1]->GetEntryOffset(), 10U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); @@ -254,11 +254,76 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor +} + +TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE; + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_flags, /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 1U); + + ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); + ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); + ASSERT_EQ(types[0]->GetEntryCount(), 1U); + ASSERT_EQ(types[0]->GetEntryOffset(), 7U); + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature +} + +TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) { + const std::string target_apk_path(GetTestDataPath() + "/target/target.apk"); + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); + + const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); + + uint32_t policy_flags = PolicyFlags::POLICY_PUBLIC; + + std::stringstream error; + std::unique_ptr<const Idmap> idmap = + Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, + policy_flags, /* enforce_overlayable */ true, error); + ASSERT_THAT(idmap, NotNull()); + + const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); + ASSERT_EQ(dataBlocks.size(), 1U); + + const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + + ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); + ASSERT_EQ(data->GetHeader()->GetTypeCount(), 0U); + + const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); + ASSERT_EQ(types.size(), 0U); // can't overlay, so contains nothing } // Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled. @@ -292,11 +357,12 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 3U); + ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); ASSERT_EQ(types[0]->GetEntry(0), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0006U); // string/policy_system_vendor } // Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled. @@ -330,14 +396,15 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgn ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 6U); + ASSERT_EQ(types[0]->GetEntryCount(), 7U); ASSERT_EQ(types[0]->GetEntryOffset(), 3U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/other ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_product - ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_system_vendor } // The resources of APKs that do not include an overlayable declaration should not restrict what @@ -371,14 +438,15 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayable) { ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 6U); + ASSERT_EQ(types[0]->GetEntryCount(), 7U); ASSERT_EQ(types[0]->GetEntryOffset(), 3U); ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/other ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_product ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/string/policy_signature + ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_system_vendor } // The resources of APKs that do not include an overlayable declaration should not restrict what @@ -418,7 +486,7 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTar ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U); ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U); ASSERT_EQ(types[1]->GetEntryCount(), 4U); - ASSERT_EQ(types[1]->GetEntryOffset(), 9U); + ASSERT_EQ(types[1]->GetEntryOffset(), 10U); ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index a5588c330d85..7ec13ed0ade7 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -52,8 +52,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos); - ASSERT_NE(stream.str().find("00000008: dd53ca29 target crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000000c: a71ccd77 overlay crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000008: d513ca1b target crc\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build index e60da803388b..e879f443c7b1 100644..100755 --- a/cmds/idmap2/tests/data/overlay/build +++ b/cmds/idmap2/tests/data/overlay/build @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FRAMEWORK_RES_APK="$(gettop)/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk" +FRAMEWORK_RES_APK="${ANDROID_BUILD_TOP}/out/target/common/obj/APPS/framework-res_intermediates/package-export.apk" aapt2 compile --dir res -o compiled.flata diff --git a/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml new file mode 100644 index 000000000000..5dacebded529 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="test.overlay.system"> + <overlay + android:targetPackage="test.target" + android:targetName="TestResources"/> +</manifest> diff --git a/packages/CompanionDeviceManager/Android.mk b/cmds/idmap2/tests/data/signature-overlay/build index 7ec6e1146060..fdd8301c3b7d 100644..100755 --- a/packages/CompanionDeviceManager/Android.mk +++ b/cmds/idmap2/tests/data/signature-overlay/build @@ -1,4 +1,4 @@ -# 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. @@ -12,17 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -LOCAL_PATH:= $(call my-dir) +FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar -include $(CLEAR_VARS) +aapt2 compile --dir res -o compiled.flata -LOCAL_MODULE_TAGS := optional +aapt2 link \ + --no-resource-removal \ + -I "$FRAMEWORK_RES_APK" \ + --manifest AndroidManifest.xml \ + -o signature-overlay.apk \ + compiled.flata -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := CompanionDeviceManager -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under, $(LOCAL_PATH)) +rm compiled.flata diff --git a/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml new file mode 100644 index 000000000000..59e7d8ed69c1 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/res/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- This overlay will fulfill the policy "signature". This allows it overlay the + following resources. --> + <string name="policy_signature">policy_signature</string> +</resources> diff --git a/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk Binary files differnew file mode 100644 index 000000000000..b2c490dcbb90 --- /dev/null +++ b/cmds/idmap2/tests/data/signature-overlay/signature-overlay.apk diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/build b/cmds/idmap2/tests/data/system-overlay-invalid/build index 920e1f8ad6f3..920e1f8ad6f3 100644..100755 --- a/cmds/idmap2/tests/data/system-overlay-invalid/build +++ b/cmds/idmap2/tests/data/system-overlay-invalid/build diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml index af1bea16daee..02704009d743 100644 --- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml +++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml @@ -22,6 +22,7 @@ <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. --> <string name="policy_product">policy_product</string> + <string name="policy_signature">policy_signature</string> <!-- Requests to overlay a resource that is not declared as overlayable. --> <string name="not_overlayable">not_overlayable</string> diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk Binary files differindex 710ed9067f69..9448939b5636 100644 --- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk +++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk diff --git a/cmds/idmap2/tests/data/system-overlay/build b/cmds/idmap2/tests/data/system-overlay/build index be0d2390f535..be0d2390f535 100644..100755 --- a/cmds/idmap2/tests/data/system-overlay/build +++ b/cmds/idmap2/tests/data/system-overlay/build diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build index 137ddb5ecaa1..e6df742cc9da 100644..100755 --- a/cmds/idmap2/tests/data/target/build +++ b/cmds/idmap2/tests/data/target/build @@ -17,5 +17,5 @@ aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata rm compiled.flata aapt2 compile res/values/values.xml -o . -aapt2 link --manifest AndroidManifest.xml -A assets -o target_no_overlayable.apk values_values.arsc.flat +aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat rm values_values.arsc.flat
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml index 02d25630546f..0bf83fa50b75 100644 --- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml +++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml @@ -15,20 +15,12 @@ --> <resources> <overlayable name="TestResources"> - <!-- Publicly overlayable resources --> - <item type="string" name="a" /> - <item type="string" name="b" /> - <item type="string" name="c" /> - <item type="string" name="str1" /> - <item type="string" name="str2" /> - <item type="string" name="str3" /> - <item type="string" name="str4" /> - <item type="string" name="x" /> - <item type="string" name="y" /> - <item type="string" name="z" /> - <item type="integer" name="int1" /> - - <!-- Resources with partition restrictins --> + <!-- Resources with signature restrictions --> + <policy type="signature"> + <item type="string" name="policy_signature" /> + </policy> + + <!-- Resources with partition restrictions --> <policy type="system"> <item type="string" name="policy_system" /> </policy> @@ -41,12 +33,26 @@ <item type="string" name="policy_product" /> </policy> + <!-- Resources publicly overlayable --> <policy type="public"> <item type="string" name="policy_public" /> + <item type="string" name="a" /> + <item type="string" name="b" /> + <item type="string" name="c" /> + <item type="string" name="str1" /> + <item type="string" name="str2" /> + <item type="string" name="str3" /> + <item type="string" name="str4" /> + <item type="string" name="x" /> + <item type="string" name="y" /> + <item type="string" name="z" /> + <item type="integer" name="int1" /> </policy> </overlayable> <overlayable name="OtherResources"> - <item type="string" name="other" /> + <policy type="public"> + <item type="string" name="other" /> + </policy> </overlayable> </resources>
\ No newline at end of file diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml index 0d337f3890a8..edd53f4c9b52 100644 --- a/cmds/idmap2/tests/data/target/res/values/values.xml +++ b/cmds/idmap2/tests/data/target/res/values/values.xml @@ -33,6 +33,7 @@ <string name="policy_system_vendor">policy_system_vendor</string> <string name="policy_product">policy_product</string> <string name="policy_public">policy_public</string> + <string name="policy_signature">policy_signature</string> <item type="string" name="other" /> </resources> diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk Binary files differindex 8676cbb9dc3f..908b54a2f0c3 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex ecbe87578684..da3c1aef5fba 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index faf2053835fa..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", @@ -238,6 +239,7 @@ cc_test { "tests/guardrail/StatsdStats_test.cpp", "tests/metrics/metrics_test_helper.cpp", "tests/statsd_test_util.cpp", + "tests/storage/StorageManager_test.cpp", "tests/e2e/WakelockDuration_e2e_test.cpp", "tests/e2e/MetricActivation_e2e_test.cpp", "tests/e2e/MetricConditionLink_e2e_test.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 b478fed49a54..c542b6215c88 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -31,6 +31,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionController.h> +#include <cutils/multiuser.h> #include <dirent.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> @@ -47,6 +48,7 @@ using namespace android; using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; namespace android { @@ -62,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(); @@ -155,7 +159,7 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) } })) { - mUidMap = new UidMap(); + mUidMap = UidMap::getInstance(); mPullerManager = new StatsPullerManager(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); @@ -1167,6 +1171,56 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p return Status::ok(); } +Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName, + int64_t trainVersionCode, int options, + int32_t state, + const std::vector<int64_t>& experimentIds) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + // For testing + if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) { + return ok(); + } + + // Caller must be granted these permissions + if (!checkCallingPermission(String16(kPermissionDump))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); + } + if (!checkCallingPermission(String16(kPermissionUsage))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); + } + // TODO: add verifier permission + + userid_t userId = multiuser_get_user_id(uid); + + bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING; + bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED; + bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + + ProtoOutputStream proto; + for (const auto& expId : experimentIds) { + proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, + (long long)expId); + } + + vector<uint8_t> buffer; + buffer.resize(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging, + rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId); + mProcessor->OnLogEvent(&event); + StorageManager::writeTrainInfo(trainVersionCode, buffer); + return Status::ok(); +} + hardware::Return<void> StatsService::reportSpeakerImpedance( const SpeakerImpedance& speakerImpedance) { LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 7f10d74ec7d6..d24565a63054 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -31,6 +31,7 @@ #include <android/frameworks/stats/1.0/types.h> #include <android/os/BnStatsManager.h> #include <android/os/IStatsCompanionService.h> +#include <android/os/IStatsManager.h> #include <binder/IResultReceiver.h> #include <utils/Looper.h> @@ -186,6 +187,13 @@ public: virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; /** + * Binder call to log BinaryPushStateChanged atom. + */ + virtual Status sendBinaryPushStateChangedAtom( + const android::String16& trainName, int64_t trainVersionCode, int options, + int32_t state, const std::vector<int64_t>& experimentIds) override; + + /** * Binder call to get SpeakerImpedance atom. */ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; 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 29f67c7e6f9b..d78647eebe1f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -245,6 +245,8 @@ 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; + HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; } // Pulled events will start at field 10000. @@ -302,6 +304,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 +2411,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 @@ -3156,6 +3191,13 @@ message FlagFlipUpdateOccurred { optional int64 order_id = 2; } +/** + * Potential experiment ids that goes with a train install. + */ +message TrainExperimentIds { + repeated int64 experiment_id = 1; +} + /* * Logs when a binary push state changes. * Logged by the installer via public api. @@ -3182,8 +3224,14 @@ message BinaryPushStateChanged { INSTALL_FAILURE = 6; INSTALL_CANCELLED = 7; INSTALLER_ROLLBACK_REQUESTED = 8; + INSTALLER_ROLLBACK_SUCCESS = 9; + INSTALLER_ROLLBACK_FAILURE = 10; } optional State state = 6; + // Possible experiment ids for monitoring this push. + optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES]; + // user id + optional int32 user_id = 8; } /** Represents USB port overheat event. */ @@ -3217,6 +3265,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 @@ -3269,6 +3339,33 @@ message ServiceLaunchReported { optional string service_name = 3; } +/** + * Logs when a hidden API is used. + * + * Logged from: + * libcore/libart/src/main/java/dalvik/system/VMRuntime.java + */ +message HiddenApiUsed { + // The uid of the app making the hidden access. + optional int32 uid = 1 [(is_uid) = true]; + + // Signature of the method or field accessed. + optional string signature = 2; + + enum AccessMethod { + NONE = 0; + REFLECTION = 1; + JNI = 2; + LINKING = 3; + } + + // Type of access. + optional AccessMethod access_method = 3; + + // Whether the access was prevented or not. + optional bool access_denied = 4; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4752,6 +4849,12 @@ message AppCompacted { // The process state at the time of compaction. optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN]; + + // Free ZRAM in kilobytes before compaction. + optional int64 before_zram_free_kilobytes = 17; + + // Free ZRAM in kilobytes after compaction. + optional int64 after_zram_free_kilobytes = 18; } /** @@ -5511,13 +5614,6 @@ message DeviceIdentifierAccessDenied { } /** - * Potential experiment ids that goes with a train install. - */ -message TrainExperimentIds { - repeated int64 experiment_id = 1; -} - -/** * Pulls the ongoing mainline install train version code. * Pulled from StatsCompanionService */ @@ -5558,3 +5654,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 40a4070d2974..dec36b54a1ce 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -20,6 +20,8 @@ #include "stats_log_util.h" #include "statslog.h" +#include <binder/IPCThreadState.h> + namespace android { namespace os { namespace statsd { @@ -180,6 +182,25 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT } } +LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId) { + mLogdTimestampNs = getWallClockNs(); + mElapsedTimestampNs = getElapsedRealtimeNs(); + mTagId = android::util::BINARY_PUSH_STATE_CHANGED; + mLogUid = android::IPCThreadState::self()->getCallingUid(); + + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled))); + mValues.push_back( + FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds))); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId))); +} + LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance) { mLogdTimestampNs = wallClockTimestampNs; @@ -340,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 784376a1580c..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. */ @@ -97,6 +102,11 @@ public: const std::map<int32_t, std::string>& string_map, const std::map<int32_t, float>& float_map); + // Constructs a BinaryPushStateChanged LogEvent from API call. + explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging, + bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, + const std::vector<uint8_t>& experimentIds, int32_t userId); + explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SpeakerImpedance& speakerImpedance); @@ -127,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 11075685b7fa..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) { @@ -107,7 +108,8 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } - if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) { + if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT && + it->second.state == ActivationState::kNotActive) { it->second.state = ActivationState::kActiveOnBoot; return; } 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 90f641a34b85..165e57c73743 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -38,10 +38,13 @@ using std::map; #define STATS_DATA_DIR "/data/misc/stats-data" #define STATS_SERVICE_DIR "/data/misc/stats-service" +#define TRAIN_INFO_DIR "/data/misc/train-info" // for ConfigMetricsReportList const int FIELD_ID_REPORTS = 2; +std::mutex StorageManager::sTrainInfoMutex; + using android::base::StringPrintf; using std::unique_ptr; @@ -92,6 +95,71 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte close(fd); } +bool StorageManager::writeTrainInfo(int64_t trainVersionCode, + const std::vector<uint8_t>& experimentIds) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + deleteAllFiles(TRAIN_INFO_DIR); + + string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode); + + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd == -1) { + VLOG("Attempt to access %s but failed", file_name.c_str()); + return false; + } + + size_t result = write(fd, experimentIds.data(), experimentIds.size()); + if (result == experimentIds.size()) { + VLOG("Successfully wrote %s", file_name.c_str()); + } else { + VLOG("Failed to write %s", file_name.c_str()); + return false; + } + + result = fchown(fd, AID_STATSD, AID_STATSD); + if (result) { + VLOG("Failed to chown %s to statsd", file_name.c_str()); + return false; + } + + close(fd); + return true; +} + +bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) { + std::lock_guard<std::mutex> lock(sTrainInfoMutex); + + unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir); + + if (dir == NULL) { + VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); + return false; + } + + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] == '.') { + continue; + } + trainInfo.trainVersionCode = StrToInt64(name); + string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name); + int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string str; + if (android::base::ReadFdToString(fd, &str)) { + close(fd); + std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds)); + VLOG("Read train info file successful: %s", fullPath.c_str()); + return true; + } + } + close(fd); + } + return false; +} + void StorageManager::deleteFile(const char* file) { if (remove(file) != 0) { VLOG("Attempt to delete %s but is not found", file); diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index dcf3bb607380..d6df8674e59b 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -29,6 +29,11 @@ namespace statsd { using android::util::ProtoOutputStream; +struct TrainInfo { + int64_t trainVersionCode; + std::vector<uint8_t> experimentIds; +}; + class StorageManager : public virtual RefBase { public: /** @@ -37,6 +42,16 @@ public: static void writeFile(const char* file, const void* buffer, int numBytes); /** + * Writes train info. + */ + static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds); + + /** + * Reads train info. + */ + static bool readTrainInfo(InstallTrainInfo& trainInfo); + + /** * Reads the file content to the buffer. */ static bool readFileToString(const char* file, string* content); @@ -109,6 +124,8 @@ private: * Prints disk usage statistics about a directory related to statsd. */ static void printDirStats(int out, const char* path); + + static std::mutex sTrainInfoMutex; }; } // namespace statsd 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/statsd.rc b/cmds/statsd/statsd.rc index e0cbd5d25716..a98ecd586b42 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -27,3 +27,4 @@ on post-fs-data mkdir /data/misc/stats-data/ 0770 statsd system mkdir /data/misc/stats-service/ 0770 statsd system mkdir /data/misc/stats-active-metric/ 0770 statsd system + mkdir /data/misc/train-info/ 0770 statsd system 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 new file mode 100644 index 000000000000..f66de0518f80 --- /dev/null +++ b/cmds/statsd/tests/storage/StorageManager_test.cpp @@ -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. + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <stdio.h> +#include "src/storage/StorageManager.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using namespace testing; +using std::make_shared; +using std::shared_ptr; +using std::vector; +using testing::Contains; + +TEST(StorageManagerTest, TrainInfoReadWriteTest) { + InstallTrainInfo trainInfo; + trainInfo.trainVersionCode = 12345; + const char* expIds = "test_ids"; + trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds)); + + StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds); + + InstallTrainInfo result; + StorageManager::readTrainInfo(result); + EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode); + EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size()); + EXPECT_EQ(trainInfo.experimentIds, result.experimentIds); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index d29e68e5a187..3180b77f5700 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -26,6 +26,8 @@ import android.os.SystemClock; import android.os.SystemProperties; public class PowerCommand extends Svc.Command { + private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0; + public PowerCommand() { super("power"); } @@ -42,7 +44,17 @@ public class PowerCommand extends Svc.Command { + " svc power reboot [reason]\n" + " Perform a runtime shutdown and reboot device with specified reason.\n" + " svc power shutdown\n" - + " Perform a runtime shutdown and power off the device.\n"; + + " Perform a runtime shutdown and power off the device.\n" + + " svc power forcesuspend [t]\n" + + " Force the system into suspend, ignoring all wakelocks.\n" + + " t - Number of milliseconds to wait before issuing force-suspend.\n" + + " Helps with devices that can't suspend while plugged in.\n" + + " Defaults to " + FORCE_SUSPEND_DELAY_DEFAULT_MILLIS + ".\n" + + " When using a delay, you must use the nohup shell modifier:\n" + + " 'adb shell nohup svc power forcesuspend [time]'\n" + + " Use caution; this is dangerous. It puts the device to sleep\n" + + " immediately without giving apps or the system an opportunity to\n" + + " save their state.\n"; } public void run(String[] args) { @@ -101,6 +113,20 @@ public class PowerCommand extends Svc.Command { maybeLogRemoteException("Failed to shutdown."); } return; + } else if ("forcesuspend".equals(args[1])) { + int delayMillis = args.length > 2 + ? Integer.parseInt(args[2]) : FORCE_SUSPEND_DELAY_DEFAULT_MILLIS; + try { + Thread.sleep(delayMillis); + if (!pm.forceSuspend()) { + System.err.println("Failed to force suspend."); + } + } catch (InterruptedException e) { + System.err.println("Failed to force suspend: " + e); + } catch (RemoteException e) { + maybeLogRemoteException("Failed to force-suspend with exception: " + e); + } + return; } } } diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt index dca3b525c06a..b328f2ac1955 100644 --- a/config/hiddenapi-force-blacklist.txt +++ b/config/hiddenapi-force-blacklist.txt @@ -1,4 +1,6 @@ Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V +Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V +Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/invoke/VarHandle;->acquireFence()V Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object; diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-greylist-max-o.txt index 4b6cc0ef7795..d9c1cd0313fc 100644 --- a/config/hiddenapi-greylist-max-o.txt +++ b/config/hiddenapi-greylist-max-o.txt @@ -109777,7 +109777,6 @@ Ldalvik/system/VMRuntime;->setHiddenApiAccessLogSamplingRate(I)V Ldalvik/system/VMRuntime;->setNonSdkApiUsageConsumer(Ljava/util/function/Consumer;)V Ldalvik/system/VMRuntime;->setProcessPackageName(Ljava/lang/String;)V Ldalvik/system/VMRuntime;->setSystemDaemonThreadPriority()V -Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V Ldalvik/system/VMRuntime;->startHeapTaskProcessor()V Ldalvik/system/VMRuntime;->startJitCompilation()V Ldalvik/system/VMRuntime;->stopHeapTaskProcessor()V diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index b72ce8977128..a836e8ef202f 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -170,7 +170,6 @@ Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo Landroid/app/IAssistDataReceiver$Stub;-><init>()V Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V -Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z Landroid/app/IInstrumentationWatcher$Stub;-><init>()V Landroid/app/IInstrumentationWatcher;->instrumentationStatus(Landroid/content/ComponentName;ILandroid/os/Bundle;)V Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V @@ -542,8 +541,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; @@ -2825,7 +2822,6 @@ Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V -Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection; Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone; diff --git a/core/java/android/annotation/CurrentTimeMillisLong.java b/core/java/android/annotation/CurrentTimeMillisLong.java index 9846ccee7a25..355bb5a2f960 100644 --- a/core/java/android/annotation/CurrentTimeMillisLong.java +++ b/core/java/android/annotation/CurrentTimeMillisLong.java @@ -25,12 +25,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; /** - * @memberDoc Value is a non-negative timestamp in the - * {@link System#currentTimeMillis()} time base. - * @paramDoc Value is a non-negative timestamp in the - * {@link System#currentTimeMillis()} time base. - * @returnDoc Value is a non-negative timestamp in the - * {@link System#currentTimeMillis()} time base. + * @memberDoc Value is a non-negative timestamp measured as the number of + * milliseconds since 1970-01-01T00:00:00Z. + * @paramDoc Value is a non-negative timestamp measured as the number of + * milliseconds since 1970-01-01T00:00:00Z. + * @returnDoc Value is a non-negative timestamp measured as the number of + * milliseconds since 1970-01-01T00:00:00Z. * @hide */ @Retention(SOURCE) diff --git a/core/java/android/annotation/CurrentTimeSecondsLong.java b/core/java/android/annotation/CurrentTimeSecondsLong.java new file mode 100644 index 000000000000..2b4ffd77ad34 --- /dev/null +++ b/core/java/android/annotation/CurrentTimeSecondsLong.java @@ -0,0 +1,39 @@ +/* + * 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.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @memberDoc Value is a non-negative timestamp measured as the number of + * seconds since 1970-01-01T00:00:00Z. + * @paramDoc Value is a non-negative timestamp measured as the number of + * seconds since 1970-01-01T00:00:00Z. + * @returnDoc Value is a non-negative timestamp measured as the number of + * seconds since 1970-01-01T00:00:00Z. + * @hide + */ +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface CurrentTimeSecondsLong { +} diff --git a/core/java/android/annotation/Px.java b/core/java/android/annotation/Px.java index ad99fdb7657e..cec7f80405d3 100644 --- a/core/java/android/annotation/Px.java +++ b/core/java/android/annotation/Px.java @@ -29,6 +29,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; * Denotes that a numeric parameter, field or method return value is expected * to represent a pixel dimension. * + * @memberDoc This units of this value are pixels. * @paramDoc This units of this value are pixels. * @returnDoc This units of this value are pixels. * {@hide} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5f778da44a1c..d29feddc0963 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 @@ -3782,14 +3783,14 @@ public class Activity extends ContextThemeWrapper /** - * Moves the activity from {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing mode to - * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. + * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing mode + * and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. * * @hide */ @Override - public void exitFreeformMode() throws RemoteException { - ActivityTaskManager.getService().exitFreeformMode(mToken); + public void toggleFreeformWindowingMode() throws RemoteException { + ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken); } /** 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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 69c450cee364..7d828d87e278 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -259,10 +259,11 @@ public abstract class ActivityManagerInternal { public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, long duration, String tag); - public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent, - String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, - boolean sticky, int userId, boolean allowBackgroundActivityStarts); + public abstract int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, + int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, + int resultCode, String resultData, Bundle resultExtras, String requiredPermission, + Bundle bOptions, boolean serialized, boolean sticky, int userId, + boolean allowBackgroundActivityStarts); public abstract ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, boolean fgRequired, String callingPackage, int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cc419b8d837a..790863713b19 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3229,8 +3229,9 @@ public final class ActivityThread extends ClientTransactionHandler { TAG, "Handling launch of " + r); // Initialize before creating the activity - if (!ThreadedRenderer.sRendererDisabled) { - GraphicsEnvironment.earlyInitEGL(); + if (!ThreadedRenderer.sRendererDisabled + && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + HardwareRenderer.preload(); } WindowManagerGlobal.initialize(); diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index ce7199816726..e0ae4e37765a 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -29,12 +29,17 @@ import android.content.Intent; import android.graphics.Insets; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; +import android.hardware.input.InputManager; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.IWindowManager; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceSession; @@ -291,6 +296,9 @@ public class ActivityView extends ViewGroup { /** Send current location and size to the WM to set tap exclude region for this view. */ private void updateLocation() { + if (!isAttachedToWindow()) { + return; + } try { getLocationInWindow(mLocationInWindow); WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), @@ -343,6 +351,32 @@ public class ActivityView extends ViewGroup { mSurfaceView.setVisibility(visibility); } + /** + * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the + * virtual display. + */ + public void performBackPress() { + if (mVirtualDisplay == null) { + return; + } + final int displayId = mVirtualDisplay.getDisplay().getDisplayId(); + final InputManager im = InputManager.getInstance(); + im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, displayId), + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, displayId), + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + private static KeyEvent createKeyEvent(int action, int code, int displayId) { + long when = SystemClock.uptimeMillis(); + final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */, + 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, + InputDevice.SOURCE_KEYBOARD); + ev.setDisplayId(displayId); + return ev; + } + private void initVirtualDisplay(SurfaceSession surfaceSession) { if (mVirtualDisplay != null) { throw new IllegalStateException("Trying to initialize for the second time."); @@ -429,6 +463,9 @@ public class ActivityView extends ViewGroup { /** Report to server that tap exclude region on hosting display should be cleared. */ private void cleanTapExcludeRegion() { + if (!isAttachedToWindow()) { + return; + } // Update tap exclude region with an empty rect to clean the state on server. try { WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 64b94a946489..040ad062e512 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -46,6 +46,8 @@ import android.util.ArrayMap; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; + +import com.android.internal.annotations.Immutable; import com.android.internal.app.IAppOpsActiveCallback; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsNotedCallback; @@ -848,6 +850,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 @@ -2209,6 +2212,115 @@ public class AppOpsManager { } /** + * Request for getting historical app op usage. The request acts + * as a filtering criteria when querying historical op usage. + * + * @hide + */ + @Immutable + @TestApi + @SystemApi + public static final class HistoricalOpsRequest { + private final int mUid; + private final @Nullable String mPackageName; + private final @Nullable List<String> mOpNames; + private final long mBeginTimeMillis; + private final long mEndTimeMillis; + + private HistoricalOpsRequest(int uid, @Nullable String packageName, + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis) { + mUid = uid; + mPackageName = packageName; + mOpNames = opNames; + mBeginTimeMillis = beginTimeMillis; + mEndTimeMillis = endTimeMillis; + } + + /** + * Builder for creating a {@link HistoricalOpsRequest}. + * + * @hide + */ + @TestApi + @SystemApi + public static final class Builder { + private int mUid = Process.INVALID_UID; + private @Nullable String mPackageName; + private @Nullable List<String> mOpNames; + private final long mBeginTimeMillis; + private final long mEndTimeMillis; + + /** + * Creates a new builder. + * + * @param beginTimeMillis The beginning of the interval in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non + * negative. + * @param endTimeMillis The end of the interval in milliseconds since + * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after + * {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent + * history including ops that happen while this call is in flight. + */ + public Builder(long beginTimeMillis, long endTimeMillis) { + Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis, + "beginTimeMillis must be non negative and lesser than endTimeMillis"); + mBeginTimeMillis = beginTimeMillis; + mEndTimeMillis = endTimeMillis; + } + + /** + * Sets the UID to query for. + * + * @param uid The uid. Pass {@link android.os.Process#INVALID_UID} for any uid. + * @return This builder. + */ + public @NonNull Builder setUid(int uid) { + Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0, + "uid must be " + Process.INVALID_UID + " or non negative"); + mUid = uid; + return this; + } + + /** + * Sets the package to query for. + * + * @param packageName The package name. <code>Null</code> for any package. + * @return This builder. + */ + public @NonNull Builder setPackageName(@Nullable String packageName) { + mPackageName = packageName; + return this; + } + + /** + * Sets the op names to query for. + * + * @param opNames The op names. <code>Null</code> for any op. + * @return This builder. + */ + public @NonNull Builder setOpNames(@Nullable List<String> opNames) { + if (opNames != null) { + final int opCount = opNames.size(); + for (int i = 0; i < opCount; i++) { + Preconditions.checkArgument(AppOpsManager.strOpToOp( + opNames.get(i)) != AppOpsManager.OP_NONE); + } + } + mOpNames = opNames; + return this; + } + + /** + * @return a new {@link HistoricalOpsRequest}. + */ + public @NonNull HistoricalOpsRequest build() { + return new HistoricalOpsRequest(mUid, mPackageName, mOpNames, + mBeginTimeMillis, mEndTimeMillis); + } + } + } + + /** * This class represents historical app op state of all UIDs for a given time interval. * * @hide @@ -3670,26 +3782,7 @@ public class AppOpsManager { /** * Retrieve historical app op stats for a period. * - * <p>Historical data can be obtained - * for a specific package by specifying the <code>packageName</code> argument, - * for a specific UID if specifying the <code>uid</code> argument, for a - * specific package in a UID by specifying the <code>packageName</code> - * and the <code>uid</code> arguments, for all packages by passing - * {@link android.os.Process#INVALID_UID} and <code>null</code> for the - * <code>uid</code> and <code>packageName</code> arguments, respectively. - * Similarly, you can specify the <code>opNames</code> argument to get - * data only for these ops or <code>null</code> for all ops. - * - * @param uid The UID to query for. - * @param packageName The package to query for. - * @param beginTimeMillis The beginning of the interval in milliseconds since - * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be non - * negative. - * @param endTimeMillis The end of the interval in milliseconds since - * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian). Must be after - * {@code beginTimeMillis}. Pass {@link Long#MAX_VALUE} to get the most recent - * history including ops that happen while this call is in flight. - * @param opNames The ops to query for. Pass {@code null} for all ops. + * @param request A request object describing the data being queried for. * @param executor Executor on which to run the callback. If <code>null</code> * the callback is executed on the default executor running on the main thread. * @param callback Callback on which to deliver the result. @@ -3701,13 +3794,13 @@ public class AppOpsManager { @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) - public void getHistoricalOps(int uid, @Nullable String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + public void getHistoricalOps(@NonNull HistoricalOpsRequest request, @NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); try { - mService.getHistoricalOps(uid, packageName, opNames, beginTimeMillis, endTimeMillis, + mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames, + request.mBeginTimeMillis, request.mEndTimeMillis, new RemoteCallback((result) -> { final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); final long identity = Binder.clearCallingIdentity(); @@ -3724,29 +3817,12 @@ public class AppOpsManager { /** * Retrieve historical app op stats for a period. - * - * <p>Historical data can be obtained - * for a specific package by specifying the <code>packageName</code> argument, - * for a specific UID if specifying the <code>uid</code> argument, for a - * specific package in a UID by specifying the <code>packageName</code> - * and the <code>uid</code> arguments, for all packages by passing - * {@link android.os.Process#INVALID_UID} and <code>null</code> for the - * <code>uid</code> and <code>packageName</code> arguments, respectively. - * Similarly, you can specify the <code>opNames</code> argument to get - * data only for these ops or <code>null</code> for all ops. * <p> * This method queries only the on disk state and the returned ops are raw, * which is their times are relative to the history start as opposed to the * epoch start. * - * @param uid The UID to query for. - * @param packageName The package to query for. - * @param beginTimeMillis The beginning of the interval in milliseconds since - * history start. History time grows as one goes into the past. - * @param endTimeMillis The end of the interval in milliseconds since - * history start. History time grows as one goes into the past. Must be after - * {@code beginTimeMillis}. - * @param opNames The ops to query for. Pass {@code null} for all ops. + * @param request A request object describing the data being queried for. * @param executor Executor on which to run the callback. If <code>null</code> * the callback is executed on the default executor running on the main thread. * @param callback Callback on which to deliver the result. @@ -3757,15 +3833,15 @@ public class AppOpsManager { */ @TestApi @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) - public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + public void getHistoricalOpsFromDiskRaw(@NonNull HistoricalOpsRequest request, @Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); try { - mService.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, beginTimeMillis, - endTimeMillis, new RemoteCallback((result) -> { - final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); + mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName, + request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis, + new RemoteCallback((result) -> { + final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS); final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> callback.accept(ops)); @@ -4290,12 +4366,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 +4593,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/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 69c3632b7ab2..062a462b71fd 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -33,6 +33,7 @@ public class BroadcastOptions { private int mMinManifestReceiverApiLevel = 0; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; private boolean mDontSendToRestrictedApps = false; + private boolean mAllowBackgroundActivityStarts; /** * How long to temporarily put an app on the power whitelist when executing this broadcast @@ -54,11 +55,17 @@ public class BroadcastOptions { = "android:broadcast.maxManifestReceiverApiLevel"; /** - * Corresponds to {@link #setMaxManifestReceiverApiLevel}. + * Corresponds to {@link #setDontSendToRestrictedApps}. */ static final String KEY_DONT_SEND_TO_RESTRICTED_APPS = "android:broadcast.dontSendToRestrictedApps"; + /** + * Corresponds to {@link #setAllowBackgroundActivityStarts}. + */ + static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS = + "android:broadcast.allowBackgroundActivityStarts"; + public static BroadcastOptions makeBasic() { BroadcastOptions opts = new BroadcastOptions(); return opts; @@ -74,6 +81,8 @@ public class BroadcastOptions { mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, Build.VERSION_CODES.CUR_DEVELOPMENT); mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false); + mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, + false); } /** @@ -148,6 +157,23 @@ public class BroadcastOptions { } /** + * Sets the process will be able to start activities from background for the duration of + * the broadcast dispatch. Default value is {@code false} + */ + @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) + public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) { + mAllowBackgroundActivityStarts = allowBackgroundActivityStarts; + } + + /** + * @hide + * @return #setAllowBackgroundActivityStarts + */ + public boolean allowsBackgroundActivityStarts() { + return mAllowBackgroundActivityStarts; + } + + /** * Returns the created options as a Bundle, which can be passed to * {@link android.content.Context#sendBroadcast(android.content.Intent) * Context.sendBroadcast(Intent)} and related methods. @@ -169,6 +195,9 @@ public class BroadcastOptions { if (mDontSendToRestrictedApps) { b.putBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, true); } + if (mAllowBackgroundActivityStarts) { + b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true); + } return b.isEmpty() ? null : b; } } 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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 2b765b2284e7..497d5ba5fd93 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -215,7 +215,7 @@ interface IActivityTaskManager { void registerTaskStackListener(in ITaskStackListener listener); void unregisterTaskStackListener(in ITaskStackListener listener); void setTaskResizeable(int taskId, int resizeableMode); - void exitFreeformMode(in IBinder token); + void toggleFreeformWindowingMode(in IBinder token); void resizeTask(int taskId, in Rect bounds, int resizeMode); void moveStackToDisplay(int stackId, int displayId); void removeStack(int stackId); 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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 806536b6947c..3587c68ef234 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -7036,9 +7036,9 @@ public class DevicePolicyManager { } /** - * Called by a profile or device owner to set the permitted input methods services. When set by - * a device owner or profile owner the restriction applies to all profiles of the user the - * device owner or profile owner is an admin for. By default, the user can use any input method. + * Called by a profile or device owner to set the permitted input methods services for this + * user. By default, the user can use any input method. + * <p> * When zero or more packages have been added, input method that are not in the list and not * part of the system can not be enabled by the user. This method will fail if it is called for * a admin that is not for the foreground user or a profile of the foreground user. Any @@ -7047,7 +7047,7 @@ public class DevicePolicyManager { * Calling with a null value for the list disables the restriction so that all input methods can * be used, calling with an empty list disables all but the system's own input methods. * <p> - * System input methods are always available to the user this method can't modify this. + * System input methods are always available to the user - this method can't modify this. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param packageNames List of input method package names. diff --git a/core/java/android/app/admin/TEST_MAPPING b/core/java/android/app/admin/TEST_MAPPING new file mode 100644 index 000000000000..8f88c2243d50 --- /dev/null +++ b/core/java/android/app/admin/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/services/devicepolicy" + } + ] +} 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/ContentInterface.java b/core/java/android/content/ContentInterface.java index 3d732eb7678d..d41d8d9cc1e2 100644 --- a/core/java/android/content/ContentInterface.java +++ b/core/java/android/content/ContentInterface.java @@ -36,6 +36,8 @@ import java.util.ArrayList; * These methods have been extracted into a general interface so that APIs can * be flexible in accepting either a {@link ContentProvider}, a * {@link ContentResolver}, or a {@link ContentProviderClient}. + * + * @hide */ public interface ContentInterface { public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index f138d39b7fb0..e06322df7a7f 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -134,7 +134,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private boolean mNoPerms; private boolean mSingleUser; - private final ThreadLocal<String> mCallingPackage = new ThreadLocal<>(); + private ThreadLocal<String> mCallingPackage; private Transport mTransport = new Transport(); @@ -2034,6 +2034,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall private void attachInfo(Context context, ProviderInfo info, boolean testing) { mNoPerms = testing; + mCallingPackage = new ThreadLocal<>(); /* * Only allow it to be set once, so after the content service gives diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 0b5bdb5bfd4f..93bf5188705c 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -626,15 +626,14 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { return ContentProvider.coerceToLocalContentProvider(mContentProvider); } - /** - * Closes the given object quietly, ignoring any checked exceptions. Does - * nothing if the given object is {@code null}. - */ + /** {@hide} */ + @Deprecated public static void closeQuietly(ContentProviderClient client) { IoUtils.closeQuietly(client); } /** {@hide} */ + @Deprecated public static void releaseQuietly(ContentProviderClient client) { IoUtils.closeQuietly(client); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 7672ccfcd995..0e11d4e12f3d 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -609,10 +609,60 @@ public abstract class ContentResolver implements ContentInterface { private static final int SLOW_THRESHOLD_MILLIS = 500; private final Random mRandom = new Random(); // guarded by itself - public ContentResolver(Context context) { + public ContentResolver(@Nullable Context context) { + this(context, null); + } + + /** {@hide} */ + public ContentResolver(@Nullable Context context, @Nullable ContentInterface wrapped) { mContext = context != null ? context : ActivityThread.currentApplication(); mPackageName = mContext.getOpPackageName(); mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + mWrapped = wrapped; + } + + /** {@hide} */ + public static ContentResolver wrap(@NonNull ContentInterface wrapped) { + Preconditions.checkNotNull(wrapped); + + return new ContentResolver(null, wrapped) { + @Override + public void unstableProviderDied(IContentProvider icp) { + throw new UnsupportedOperationException(); + } + @Override + public boolean releaseUnstableProvider(IContentProvider icp) { + throw new UnsupportedOperationException(); + } + @Override + public boolean releaseProvider(IContentProvider icp) { + throw new UnsupportedOperationException(); + } + @Override + protected IContentProvider acquireUnstableProvider(Context c, String name) { + throw new UnsupportedOperationException(); + } + @Override + protected IContentProvider acquireProvider(Context c, String name) { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Create a {@link ContentResolver} instance that redirects all its methods + * to the given {@link ContentProvider}. + */ + public static ContentResolver wrap(@NonNull ContentProvider wrapped) { + return wrap((ContentInterface) wrapped); + } + + /** + * Create a {@link ContentResolver} instance that redirects all its methods + * to the given {@link ContentProviderClient}. + */ + public static ContentResolver wrap(@NonNull ContentProviderClient wrapped) { + return wrap((ContentInterface) wrapped); } /** @hide */ @@ -660,6 +710,12 @@ public abstract class ContentResolver implements ContentInterface { public final @Nullable String getType(@NonNull Uri url) { Preconditions.checkNotNull(url, "url"); + try { + if (mWrapped != null) return mWrapped.getType(url); + } catch (RemoteException e) { + return null; + } + // XXX would like to have an acquireExistingUnstableProvider for this. IContentProvider provider = acquireExistingProvider(url); if (provider != null) { @@ -715,6 +771,12 @@ public abstract class ContentResolver implements ContentInterface { Preconditions.checkNotNull(url, "url"); Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter"); + try { + if (mWrapped != null) return mWrapped.getStreamTypes(url, mimeTypeFilter); + } catch (RemoteException e) { + return null; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { return null; @@ -843,6 +905,15 @@ public abstract class ContentResolver implements ContentInterface { @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); + + try { + if (mWrapped != null) { + return mWrapped.query(uri, projection, queryArgs, cancellationSignal); + } + } catch (RemoteException e) { + return null; + } + IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; @@ -942,6 +1013,13 @@ public abstract class ContentResolver implements ContentInterface { @Override public final @Nullable Uri canonicalize(@NonNull Uri url) { Preconditions.checkNotNull(url, "url"); + + try { + if (mWrapped != null) return mWrapped.canonicalize(url); + } catch (RemoteException e) { + return null; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { return null; @@ -979,6 +1057,13 @@ public abstract class ContentResolver implements ContentInterface { @Override public final @Nullable Uri uncanonicalize(@NonNull Uri url) { Preconditions.checkNotNull(url, "url"); + + try { + if (mWrapped != null) return mWrapped.uncanonicalize(url); + } catch (RemoteException e) { + return null; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { return null; @@ -1015,6 +1100,13 @@ public abstract class ContentResolver implements ContentInterface { public final boolean refresh(@NonNull Uri url, @Nullable Bundle args, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(url, "url"); + + try { + if (mWrapped != null) return mWrapped.refresh(url, args, cancellationSignal); + } catch (RemoteException e) { + return false; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { return false; @@ -1126,6 +1218,12 @@ public abstract class ContentResolver implements ContentInterface { @Override public final @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode, @Nullable CancellationSignal signal) throws FileNotFoundException { + try { + if (mWrapped != null) return mWrapped.openFile(uri, mode, signal); + } catch (RemoteException e) { + return null; + } + return openFileDescriptor(uri, mode, signal); } @@ -1237,6 +1335,12 @@ public abstract class ContentResolver implements ContentInterface { @Override public final @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode, @Nullable CancellationSignal signal) throws FileNotFoundException { + try { + if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, signal); + } catch (RemoteException e) { + return null; + } + return openAssetFileDescriptor(uri, mode, signal); } @@ -1448,6 +1552,14 @@ public abstract class ContentResolver implements ContentInterface { public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts, @Nullable CancellationSignal signal) throws FileNotFoundException { + try { + if (mWrapped != null) { + return mWrapped.openTypedAssetFile(uri, mimeTypeFilter, opts, signal); + } + } catch (RemoteException e) { + return null; + } + return openTypedAssetFileDescriptor(uri, mimeTypeFilter, opts, signal); } @@ -1664,6 +1776,13 @@ public abstract class ContentResolver implements ContentInterface { public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url, @Nullable ContentValues values) { Preconditions.checkNotNull(url, "url"); + + try { + if (mWrapped != null) return mWrapped.insert(url, values); + } catch (RemoteException e) { + return null; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); @@ -1705,6 +1824,13 @@ public abstract class ContentResolver implements ContentInterface { throws RemoteException, OperationApplicationException { Preconditions.checkNotNull(authority, "authority"); Preconditions.checkNotNull(operations, "operations"); + + try { + if (mWrapped != null) return mWrapped.applyBatch(authority, operations); + } catch (RemoteException e) { + return null; + } + ContentProviderClient provider = acquireContentProviderClient(authority); if (provider == null) { throw new IllegalArgumentException("Unknown authority " + authority); @@ -1731,6 +1857,13 @@ public abstract class ContentResolver implements ContentInterface { @NonNull ContentValues[] values) { Preconditions.checkNotNull(url, "url"); Preconditions.checkNotNull(values, "values"); + + try { + if (mWrapped != null) return mWrapped.bulkInsert(url, values); + } catch (RemoteException e) { + return 0; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); @@ -1764,6 +1897,13 @@ public abstract class ContentResolver implements ContentInterface { public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs) { Preconditions.checkNotNull(url, "url"); + + try { + if (mWrapped != null) return mWrapped.delete(url, where, selectionArgs); + } catch (RemoteException e) { + return 0; + } + IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); @@ -1801,6 +1941,13 @@ public abstract class ContentResolver implements ContentInterface { @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs) { Preconditions.checkNotNull(uri, "uri"); + + try { + if (mWrapped != null) return mWrapped.update(uri, values, where, selectionArgs); + } catch (RemoteException e) { + return 0; + } + IContentProvider provider = acquireProvider(uri); if (provider == null) { throw new IllegalArgumentException("Unknown URI " + uri); @@ -1844,6 +1991,13 @@ public abstract class ContentResolver implements ContentInterface { @Nullable String arg, @Nullable Bundle extras) { Preconditions.checkNotNull(authority, "authority"); Preconditions.checkNotNull(method, "method"); + + try { + if (mWrapped != null) return mWrapped.call(authority, method, arg, extras); + } catch (RemoteException e) { + return null; + } + IContentProvider provider = acquireProvider(authority); if (provider == null) { throw new IllegalArgumentException("Unknown authority " + authority); @@ -2994,14 +3148,17 @@ public abstract class ContentResolver implements ContentInterface { } /** - * Put the cache with the key. + * Store the given {@link Bundle} as a long-lived cached object within the + * system. This can be useful to avoid expensive re-parsing when apps are + * restarted multiple times on low-RAM devices. + * <p> + * The {@link Bundle} is automatically invalidated when a + * {@link #notifyChange(Uri, ContentObserver)} event applies to the key. * - * @param key the key to add - * @param value the value to add - * {@hide} + * @hide */ @SystemApi - public void putCache(Uri key, Bundle value) { + public void putCache(@NonNull Uri key, @Nullable Bundle value) { try { getContentService().putCache(mContext.getPackageName(), key, value, mContext.getUserId()); @@ -3011,15 +3168,16 @@ public abstract class ContentResolver implements ContentInterface { } /** - * Get the cache with the key. + * Retrieve the last {@link Bundle} stored as a long-lived cached object + * within the system. * - * @param key the key to get the value - * @return the matched value. If the key doesn't exist, will return null. - * @see #putCache(Uri, Bundle) - * {@hide} + * @return {@code null} if no cached object has been stored, or if the + * stored object has been invalidated due to a + * {@link #notifyChange(Uri, ContentObserver)} event. + * @hide */ @SystemApi - public Bundle getCache(Uri key) { + public @Nullable Bundle getCache(@NonNull Uri key) { try { final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key, mContext.getUserId()); @@ -3193,6 +3351,7 @@ public abstract class ContentResolver implements ContentInterface { @UnsupportedAppUsage final String mPackageName; final int mTargetSdkVersion; + final ContentInterface mWrapped; private static final String TAG = "ContentResolver"; diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java index fd7b37210822..767d3f668313 100644 --- a/core/java/android/content/ContentUris.java +++ b/core/java/android/content/ContentUris.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.NonNull; import android.net.Uri; import java.util.List; @@ -83,7 +84,7 @@ public class ContentUris { * @return the long conversion of the last segment or -1 if the path is * empty */ - public static long parseId(Uri contentUri) { + public static long parseId(@NonNull Uri contentUri) { String last = contentUri.getLastPathSegment(); return last == null ? -1 : Long.parseLong(last); } @@ -96,7 +97,7 @@ public class ContentUris { * * @return the given builder */ - public static Uri.Builder appendId(Uri.Builder builder, long id) { + public static @NonNull Uri.Builder appendId(@NonNull Uri.Builder builder, long id) { return builder.appendEncodedPath(String.valueOf(id)); } @@ -108,7 +109,7 @@ public class ContentUris { * * @return a new URI with the given ID appended to the end of the path */ - public static Uri withAppendedId(Uri contentUri, long id) { + public static @NonNull Uri withAppendedId(@NonNull Uri contentUri, long id) { return appendId(contentUri.buildUpon(), id).build(); } @@ -120,7 +121,7 @@ public class ContentUris { * @throws IllegalArgumentException when the given URI has no ID to remove * from the end of the path */ - public static Uri removeId(Uri contentUri) { + public static @NonNull Uri removeId(@NonNull Uri contentUri) { // Verify that we have a valid ID to actually remove final String last = contentUri.getLastPathSegment(); if (last == null) { 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..17dc4800cb0a 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -37,6 +37,7 @@ import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; +import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1790,6 +1791,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 +2110,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). @@ -3049,18 +3095,25 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_SCANNER_FINISHED = "android.intent.action.MEDIA_SCANNER_FINISHED"; - /** - * Broadcast Action: Request the media scanner to scan a file and add it to the media database. - * The path to the file is contained in the Intent.mData field. + /** + * Broadcast Action: Request the media scanner to scan a file and add it to + * the media database. + * <p> + * The path to the file is contained in {@link Intent#getData()}. + * + * @deprecated Starting in the {@link android.os.Build.VERSION_CODES#Q} + * release, shared storage paths are sandboxed per application, + * and this broadcast cannot correctly translate those sandboxed + * paths. Callers will need to instead migrate to using + * {@link MediaScannerConnection}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE"; - /** - * Broadcast Action: Request the media scanner to scan a storage volume and add it to the media database. - * The path to the storage volume is contained in the Intent.mData field. - */ + /** @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME"; /** 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 eaf6c5a9d1cc..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 @@ -5910,24 +5917,24 @@ public abstract class PackageManager { /** * Flag to denote no restrictions. This should be used to clear any restrictions that may have * been previously set for the package. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_NONE = 0x0; /** * Flag to denote that a package should be hidden from any suggestions to the user. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001; /** * Flag to denote that a package's notifications should be hidden. - * @see PackageManager.DistractionRestriction * @hide + * @see #setDistractingPackageRestrictions(String[], int) */ @SystemApi public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002; @@ -5939,7 +5946,6 @@ public abstract class PackageManager { * @see #setDistractingPackageRestrictions(String[], int) * @hide */ - @SystemApi @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = { RESTRICTION_NONE, RESTRICTION_HIDE_FROM_SUGGESTIONS, @@ -5957,14 +5963,16 @@ public abstract class PackageManager { * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API. * * @param packages Packages to mark as distracting. - * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to - * impose on the given packages. {@link #RESTRICTION_NONE} can be used - * to clear any existing restrictions. + * @param restrictionFlags Any combination of restrictions to impose on the given packages. + * {@link #RESTRICTION_NONE} can be used to clear any existing + * restrictions. * @return A list of packages that could not have the {@code restrictionFlags} set. The system * may prevent restricting critical packages to preserve normal device function. * - * @see DistractionRestriction * @hide + * @see #RESTRICTION_NONE + * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS + * @see #RESTRICTION_HIDE_NOTIFICATIONS */ @SystemApi @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) 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/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index ceeecbc311d8..0e869c8c19fd 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -48,6 +48,9 @@ import java.io.File; * * <p class="note"><strong>Note:</strong> this class assumes * monotonically increasing version numbers for upgrades.</p> + * + * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was + * first added in the {@link android.os.Build.VERSION_CODES#Q} release.</p> */ public abstract class SQLiteOpenHelper implements AutoCloseable { private static final String TAG = SQLiteOpenHelper.class.getSimpleName(); diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 5722e7b25fbf..03e850716bc2 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -68,15 +68,23 @@ public class SQLiteQueryBuilder { } /** - * Mark the query as DISTINCT. + * Mark the query as {@code DISTINCT}. * - * @param distinct if true the query is DISTINCT, otherwise it isn't + * @param distinct if true the query is {@code DISTINCT}, otherwise it isn't */ public void setDistinct(boolean distinct) { mDistinct = distinct; } /** + * Get if the query is marked as {@code 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 @@ -98,13 +106,13 @@ public class SQLiteQueryBuilder { } /** - * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded - * by parenthesis and ANDed with the selection passed to {@link #query}. The final - * WHERE clause looks like: - * + * Append a chunk to the {@code WHERE} clause of the query. All chunks appended are surrounded + * by parenthesis and {@code AND}ed with the selection passed to {@link #query}. The final + * {@code WHERE} clause looks like: + * <p> * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>) * - * @param inWhere the chunk of text to append to the WHERE clause. + * @param inWhere the chunk of text to append to the {@code WHERE} clause. */ public void appendWhere(@NonNull CharSequence inWhere) { if (mWhereClause == null) { @@ -114,13 +122,13 @@ public class SQLiteQueryBuilder { } /** - * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded + * Append a chunk to the {@code WHERE} clause of the query. All chunks appended are surrounded * by parenthesis and ANDed with the selection passed to {@link #query}. The final - * WHERE clause looks like: - * + * {@code WHERE} clause looks like: + * <p> * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>) * - * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped + * @param inWhere the chunk of text to append to the {@code WHERE} clause. it will be escaped * to avoid SQL injection attacks */ public void appendWhereEscapeString(@NonNull String inWhere) { @@ -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. @@ -222,21 +264,21 @@ public class SQLiteQueryBuilder { * return all columns, which is discouraged to prevent reading * data from storage that isn't going to be used. * @param where A filter declaring which rows to return, formatted as an SQL - * WHERE clause (excluding the WHERE itself). Passing null will + * {@code WHERE} clause (excluding the {@code WHERE} itself). Passing {@code null} will * return all rows for the given URL. * @param groupBy A filter declaring how to group rows, formatted as an SQL - * GROUP BY clause (excluding the GROUP BY itself). Passing null + * {@code GROUP BY} clause (excluding the {@code GROUP BY} itself). Passing {@code null} * will cause the rows to not be grouped. * @param having A filter declare which row groups to include in the cursor, - * if row grouping is being used, formatted as an SQL HAVING - * clause (excluding the HAVING itself). Passing null will cause + * if row grouping is being used, formatted as an SQL {@code HAVING} + * clause (excluding the {@code HAVING} itself). Passing null will cause * all row groups to be included, and is required when row * grouping is not being used. - * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause - * (excluding the ORDER BY itself). Passing null will use the + * @param orderBy How to order the rows, formatted as an SQL {@code ORDER BY} clause + * (excluding the {@code ORDER BY} itself). Passing null will use the * default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, - * formatted as LIMIT clause. Passing null denotes no LIMIT clause. + * formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause. * @return the SQL query string */ public static String buildQueryString( @@ -308,22 +350,22 @@ public class SQLiteQueryBuilder { * null will return all columns, which is discouraged to prevent * reading data from storage that isn't going to be used. * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given URL. * @param selectionArgs You may include ?s in selection, which * will be replaced by the values from selectionArgs, in order * that they appear in the selection. The values will be bound * as Strings. * @param groupBy A filter declaring how to group rows, formatted - * as an SQL GROUP BY clause (excluding the GROUP BY + * as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} * itself). Passing null will cause the rows to not be grouped. * @param having A filter declare which row groups to include in * the cursor, if row grouping is being used, formatted as an - * SQL HAVING clause (excluding the HAVING itself). Passing + * SQL {@code HAVING} clause (excluding the {@code HAVING} itself). Passing * null will cause all row groups to be included, and is * required when row grouping is not being used. * @param sortOrder How to order the rows, formatted as an SQL - * ORDER BY clause (excluding the ORDER BY itself). Passing null + * {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null * will use the default sort order, which may be unordered. * @return a cursor over the result set * @see android.content.ContentResolver#query(android.net.Uri, String[], @@ -345,25 +387,25 @@ public class SQLiteQueryBuilder { * null will return all columns, which is discouraged to prevent * reading data from storage that isn't going to be used. * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given URL. * @param selectionArgs You may include ?s in selection, which * will be replaced by the values from selectionArgs, in order * that they appear in the selection. The values will be bound * as Strings. * @param groupBy A filter declaring how to group rows, formatted - * as an SQL GROUP BY clause (excluding the GROUP BY + * as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} * itself). Passing null will cause the rows to not be grouped. * @param having A filter declare which row groups to include in * the cursor, if row grouping is being used, formatted as an - * SQL HAVING clause (excluding the HAVING itself). Passing + * SQL {@code HAVING} clause (excluding the {@code HAVING} itself). Passing * null will cause all row groups to be included, and is * required when row grouping is not being used. * @param sortOrder How to order the rows, formatted as an SQL - * ORDER BY clause (excluding the ORDER BY itself). Passing null + * {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null * will use the default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, - * formatted as LIMIT clause. Passing null denotes no LIMIT clause. + * formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause. * @return a cursor over the result set * @see android.content.ContentResolver#query(android.net.Uri, String[], * String, String[], String) @@ -384,25 +426,25 @@ public class SQLiteQueryBuilder { * null will return all columns, which is discouraged to prevent * reading data from storage that isn't going to be used. * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given URL. * @param selectionArgs You may include ?s in selection, which * will be replaced by the values from selectionArgs, in order * that they appear in the selection. The values will be bound * as Strings. * @param groupBy A filter declaring how to group rows, formatted - * as an SQL GROUP BY clause (excluding the GROUP BY + * as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} * itself). Passing null will cause the rows to not be grouped. * @param having A filter declare which row groups to include in * the cursor, if row grouping is being used, formatted as an - * SQL HAVING clause (excluding the HAVING itself). Passing + * SQL {@code HAVING} clause (excluding the {@code HAVING} itself). Passing * null will cause all row groups to be included, and is * required when row grouping is not being used. * @param sortOrder How to order the rows, formatted as an SQL - * ORDER BY clause (excluding the ORDER BY itself). Passing null + * {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null * will use the default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, - * formatted as LIMIT clause. Passing null denotes no LIMIT clause. + * formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause. * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. @@ -467,7 +509,7 @@ public class SQLiteQueryBuilder { * * @param db the database to update on * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given URL. * @param selectionArgs You may include ?s in selection, which * will be replaced by the values from selectionArgs, in order @@ -537,7 +579,7 @@ public class SQLiteQueryBuilder { * * @param db the database to delete on * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given URL. * @param selectionArgs You may include ?s in selection, which * will be replaced by the values from selectionArgs, in order @@ -589,8 +631,8 @@ public class SQLiteQueryBuilder { } /** - * Construct a SELECT statement suitable for use in a group of - * SELECT statements that will be joined through UNION operators + * Construct a {@code SELECT} statement suitable for use in a group of + * {@code SELECT} statements that will be joined through {@code UNION} operators * in buildUnionQuery. * * @param projectionIn A list of which columns to return. Passing @@ -598,23 +640,23 @@ public class SQLiteQueryBuilder { * prevent reading data from storage that isn't going to be * used. * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given * URL. * @param groupBy A filter declaring how to group rows, formatted - * as an SQL GROUP BY clause (excluding the GROUP BY itself). + * as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} itself). * Passing null will cause the rows to not be grouped. * @param having A filter declare which row groups to include in * the cursor, if row grouping is being used, formatted as an - * SQL HAVING clause (excluding the HAVING itself). Passing + * SQL {@code HAVING} clause (excluding the {@code HAVING} itself). Passing * null will cause all row groups to be included, and is * required when row grouping is not being used. * @param sortOrder How to order the rows, formatted as an SQL - * ORDER BY clause (excluding the ORDER BY itself). Passing null + * {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing null * will use the default sort order, which may be unordered. * @param limit Limits the number of rows returned by the query, - * formatted as LIMIT clause. Passing null denotes no LIMIT clause. - * @return the resulting SQL SELECT statement + * formatted as {@code LIMIT} clause. Passing null denotes no {@code LIMIT} clause. + * @return the resulting SQL {@code SELECT} statement */ public String buildQuery( String[] projectionIn, String selection, String groupBy, @@ -677,8 +719,8 @@ public class SQLiteQueryBuilder { } /** - * Construct a SELECT statement suitable for use in a group of - * SELECT statements that will be joined through UNION operators + * Construct a {@code SELECT} statement suitable for use in a group of + * {@code SELECT} statements that will be joined through {@code UNION} operators * in buildUnionQuery. * * @param typeDiscriminatorColumn the name of the result column @@ -686,8 +728,8 @@ public class SQLiteQueryBuilder { * each row was drawn. * @param unionColumns the names of the columns to appear in the * result. This may include columns that do not appear in the - * table this SELECT is querying (i.e. mTables), but that do - * appear in one of the other tables in the UNION query that we + * table this {@code SELECT} is querying (i.e. mTables), but that do + * appear in one of the other tables in the {@code UNION} query that we * are constructing. * @param columnsPresentInTable a Set of the names of the columns * that appear in this table (i.e. in the table whose name is @@ -702,18 +744,18 @@ public class SQLiteQueryBuilder { * @param typeDiscriminatorValue the value used for the * type-discriminator column in this subquery * @param selection A filter declaring which rows to return, - * formatted as an SQL WHERE clause (excluding the WHERE + * formatted as an SQL {@code WHERE} clause (excluding the {@code WHERE} * itself). Passing null will return all rows for the given * URL. * @param groupBy A filter declaring how to group rows, formatted - * as an SQL GROUP BY clause (excluding the GROUP BY itself). + * as an SQL {@code GROUP BY} clause (excluding the {@code GROUP BY} itself). * Passing null will cause the rows to not be grouped. * @param having A filter declare which row groups to include in * the cursor, if row grouping is being used, formatted as an - * SQL HAVING clause (excluding the HAVING itself). Passing + * SQL {@code HAVING} clause (excluding the {@code HAVING} itself). Passing * null will cause all row groups to be included, and is * required when row grouping is not being used. - * @return the resulting SQL SELECT statement + * @return the resulting SQL {@code SELECT} statement */ public String buildUnionSubQuery( String typeDiscriminatorColumn, @@ -771,18 +813,18 @@ public class SQLiteQueryBuilder { } /** - * Given a set of subqueries, all of which are SELECT statements, + * Given a set of subqueries, all of which are {@code SELECT} statements, * construct a query that returns the union of what those * subqueries return. - * @param subQueries an array of SQL SELECT statements, all of + * @param subQueries an array of SQL {@code SELECT} statements, all of * which must have the same columns as the same positions in * their results * @param sortOrder How to order the rows, formatted as an SQL - * ORDER BY clause (excluding the ORDER BY itself). Passing + * {@code ORDER BY} clause (excluding the {@code ORDER BY} itself). Passing * null will use the default sort order, which may be unordered. * @param limit The limit clause, which applies to the entire union result set * - * @return the resulting SQL SELECT statement + * @return the resulting SQL {@code SELECT} statement */ public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) { StringBuilder query = new StringBuilder(128); 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/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java index f413d7cc92af..0c07a67e917d 100644 --- a/core/java/android/hardware/display/ColorDisplayManager.java +++ b/core/java/android/hardware/display/ColorDisplayManager.java @@ -65,7 +65,8 @@ public final class ColorDisplayManager { @SystemApi public static final int CAPABILITY_NONE = 0x0; /** - * The device can properly apply transforms over protected content. + * The device can use GPU composition on protected content (layers whose buffers are protected + * in the trusted memory zone). * * @hide */ diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 44b653c314ad..8a0a9c7c9d9d 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -563,7 +563,8 @@ public final class DisplayManager { * 0 produces a grayscale image, 1 is normal. * * @hide - * @deprecated use {@link ColorDisplayManager#setSaturationLevel(int)}. + * @deprecated use {@link ColorDisplayManager#setSaturationLevel(int)} instead. The level passed + * as a parameter here will be rounded to the nearest hundredth. */ @SystemApi @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION) 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/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 97868fa268ad..64448fd98bf3 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -16,7 +16,6 @@ package android.hardware.input; -import android.app.IInputForwarder; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.KeyboardLayout; import android.hardware.input.IInputDevicesChangedListener; @@ -82,7 +81,4 @@ interface IInputManager { void setCustomPointerIcon(in PointerIcon icon); void requestPointerCapture(IBinder windowToken, boolean enabled); - - /** Create input forwarder to deliver touch events to owned display. */ - IInputForwarder createInputForwarder(int displayId); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index bfb7c58c2dc9..fec5c34fab28 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -21,7 +21,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; -import android.app.IInputForwarder; import android.content.Context; import android.media.AudioAttributes; import android.os.Binder; @@ -934,26 +933,6 @@ public final class InputManager { } } - - /** - * Create an {@link IInputForwarder} targeted to provided display. - * {@link android.Manifest.permission.INJECT_EVENTS} permission is required to call this method. - * - * @param displayId Id of the target display where input events should be forwarded. - * Display must exist and must be owned by the caller. - * @return The forwarder instance. - * - * @hide - */ - @UnsupportedAppUsage - public IInputForwarder createInputForwarder(int displayId) { - try { - return mIm.createInputForwarder(displayId); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - private void populateInputDevicesLocked() { if (mInputDevicesChangedListener == null) { final InputDevicesChangedListener listener = new InputDevicesChangedListener(); 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/Debug.java b/core/java/android/os/Debug.java index aba81af00282..149ef54462dc 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -2439,4 +2439,11 @@ public final class Debug VMDebug.attachAgent(library + "=" + options, classLoader); } } + + /** + * Return the current free ZRAM usage in kilobytes. + * + * @hide + */ + public static native long getZramFreeKb(); } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 3feccf507ff5..0aed981edf92 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -388,7 +388,7 @@ public class Environment { /** {@hide} */ public static File getDataStagingDirectory(String volumeUuid) { - return new File(getDataDirectory(volumeUuid), "staging"); + return new File(getDataDirectory(volumeUuid), "pkg_staging"); } /** {@hide} */ diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 0384faa88be5..316572cfd14e 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -85,7 +85,7 @@ import java.util.zip.CheckedInputStream; /** * Utility methods useful for working with files. */ -public class FileUtils { +public final class FileUtils { private static final String TAG = "FileUtils"; /** {@hide} */ public static final int S_IRWXU = 00700; @@ -309,6 +309,7 @@ public class FileUtils { * kernel before falling back to a userspace copy as a last resort. * * @return number of bytes copied. + * @hide */ public static long copy(@NonNull File from, @NonNull File to) throws IOException { return copy(from, to, null, null, null); @@ -324,6 +325,7 @@ public class FileUtils { * @param executor that listener events should be delivered via. * @param listener to be periodically notified as the copy progresses. * @return number of bytes copied. + * @hide */ public static long copy(@NonNull File from, @NonNull File to, @Nullable CancellationSignal signal, @Nullable Executor executor, diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 8813e400bee0..4a14eced7d87 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -25,16 +25,10 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; -import android.gamedriver.GameDriverProto.Blacklist; -import android.gamedriver.GameDriverProto.Blacklists; -import android.opengl.EGL14; import android.provider.Settings; -import android.util.Base64; import android.util.Log; import android.widget.Toast; -import com.android.framework.protobuf.InvalidProtocolBufferException; - import dalvik.system.VMRuntime; import java.io.File; @@ -66,11 +60,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_BLACKLIST_FLAG = "blacklist"; - private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; + private static final String GAME_DRIVER_WHITELIST_ALL = "*"; private ClassLoader mClassLoader; private String mLayerPath; @@ -85,8 +79,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); } } @@ -580,8 +575,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; @@ -630,43 +625,23 @@ 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."); } return false; } - if (!isOptIn) { - // At this point, the application is on the whitelist only, check whether it's - // on the blacklist, terminate early when it's on the blacklist. - try { - // TODO(b/121350991) Switch to DeviceConfig with property listener. - final String base64String = - coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST); - if (base64String != null && !base64String.isEmpty()) { - final Blacklists blacklistsProto = - Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); - final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList(); - final long driverVersionCode = driverAppInfo.longVersionCode; - for (Blacklist blacklist : blacklists) { - if (blacklist.getVersionCode() == driverVersionCode) { - for (String pkgName : blacklist.getPackageNamesList()) { - if (pkgName == packageName) { - return false; - } - } - break; - } - } - } - } catch (InvalidProtocolBufferException e) { - if (DEBUG) { - Log.w(TAG, "Can't parse blacklist, skip and continue..."); - } - } + // If the application is not opted-in and check whether it's on the blacklist, + // terminate early if it's on the blacklist and fallback to system driver. + if (!isOptIn + && getGlobalSettingsString(null, coreSettings, + Settings.Global.GAME_DRIVER_BLACKLIST) + .contains(ai.packageName)) { + return false; } } @@ -681,9 +656,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); @@ -695,26 +667,13 @@ public class GraphicsEnvironment { if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); setDriverPath(paths); - return true; - } + final String driverBuildDate = driverAppInfo.metaData == null + ? "" + : driverAppInfo.metaData.getString("driver_build_date"); + setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, + driverBuildDate == null ? "" : driverBuildDate, packageName); - /** - * Start a background thread to initialize EGL. - * - * Initializing EGL involves loading and initializing the graphics driver. Some drivers take - * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render - * its first frame adds directly to user-visible app launch latency. By starting it earlier - * on a separate thread, it can usually be finished well before the UI is ready to be drawn. - * - * Should only be called after chooseDriver(). - */ - public static void earlyInitEGL() { - final Thread eglInitThread = new Thread( - () -> { - EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); - }, - "EGL Init"); - eglInitThread.start(); + return true; } private static String chooseAbi(ApplicationInfo ai) { @@ -736,7 +695,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/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index bdef57518ac9..483c41a8e48a 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -74,4 +74,7 @@ interface IPowerManager // controls whether PowerManager should doze after the screen turns off or not void setDozeAfterScreenOff(boolean on); + + // Forces the system to suspend even if there are held wakelocks. + boolean forceSuspend(); } diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index f1bba1ab2977..6d4c5a034b54 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -196,4 +196,25 @@ interface IStatsManager { * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS */ oneway void unregisterPullerCallback(int atomTag, String packageName); + + /** + * The install requires staging. + */ + const int FLAG_REQUIRE_STAGING = 0x01; + + /** + * Rollback is enabled with this install. + */ + const int FLAG_ROLLBACK_ENABLED = 0x02; + + /** + * Requires low latency monitoring. + */ + const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04; + + /** + * Logs an event for binary push for module updates. + */ + oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode, + in int options, in int state, in long[] experimentId); } 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..6bd1f2b59fad 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -363,10 +363,15 @@ public final class PowerManager { public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1; /** + * @hide + */ + public static final int GO_TO_SLEEP_REASON_MIN = 0; + + /** * Go to sleep reason code: Going to sleep due by application request. * @hide */ - public static final int GO_TO_SLEEP_REASON_APPLICATION = 0; + public static final int GO_TO_SLEEP_REASON_APPLICATION = GO_TO_SLEEP_REASON_MIN; /** * Go to sleep reason code: Going to sleep due by request of the @@ -412,6 +417,17 @@ public final class PowerManager { public static final int GO_TO_SLEEP_REASON_ACCESSIBILITY = 7; /** + * Go to sleep reason code: Going to sleep due to force-suspend. + * @hide + */ + public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8; + + /** + * @hide + */ + public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_FORCE_SUSPEND; + + /** * @hide */ public static String sleepReasonToString(int sleepReason) { @@ -424,6 +440,7 @@ public final class PowerManager { case GO_TO_SLEEP_REASON_HDMI: return "hdmi"; case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button"; case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility"; + case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend"; default: return Integer.toString(sleepReason); } } @@ -1784,7 +1801,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) { @@ -1853,6 +1870,32 @@ public final class PowerManager { } /** + * Forces the device to go to suspend, even if there are currently wakelocks being held. + * <b>Caution</b> + * This is a very dangerous command as it puts the device to sleep immediately. Apps and parts + * of the system will not be notified and will not have an opportunity to save state prior to + * the device going to suspend. + * This method should only be used in very rare circumstances where the device is intended + * to appear as completely off to the user and they have a well understood, reliable way of + * re-enabling it. + * </p><p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @return true on success, false otherwise. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public boolean forceSuspend() { + try { + return mService.forceSuspend(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes. * This broadcast is only sent to registered receivers. */ diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 8492b0cf58e2..3ee54ceebaa9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -32,6 +32,7 @@ import android.content.pm.PackageManager; import android.provider.Settings; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; +import android.text.format.DateFormat; import android.util.Log; import android.view.Display; import android.view.WindowManager; @@ -762,7 +763,8 @@ public class RecoverySystem { String reasonArg = null; if (!TextUtils.isEmpty(reason)) { - reasonArg = "--reason=" + sanitizeArg(reason); + String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); + reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); } final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index ee3d35427b29..1de811792e7d 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.net.LocalSocket; import android.net.LocalSocketAddress; +import android.provider.DeviceConfig; import android.util.Log; import android.util.Slog; @@ -66,16 +67,6 @@ public class ZygoteProcess { /** * @hide for internal use only. */ - public static final String ZYGOTE_SOCKET_NAME = "zygote"; - - /** - * @hide for internal use only. - */ - public static final String ZYGOTE_SECONDARY_SOCKET_NAME = "zygote_secondary"; - - /** - * @hide for internal use only. - */ public static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000; /** @@ -89,17 +80,12 @@ public class ZygoteProcess { /** * @hide for internal use only */ - public static final String BLASTULA_POOL_SOCKET_NAME = "blastula_pool"; - - /** - * @hide for internal use only - */ - public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; + private static final String LOG_TAG = "ZygoteProcess"; /** - * @hide for internal use only + * The default value for enabling the blastula pool. */ - private static final String LOG_TAG = "ZygoteProcess"; + private static final String BLASTULA_POOL_ENABLED_DEFAULT = "false"; /** * The name of the socket used to communicate with the primary zygote. @@ -110,6 +96,7 @@ public class ZygoteProcess { * The name of the secondary (alternate ABI) zygote socket. */ private final LocalSocketAddress mZygoteSecondarySocketAddress; + /** * The name of the socket used to communicate with the primary blastula pool. */ @@ -122,17 +109,21 @@ public class ZygoteProcess { public ZygoteProcess() { mZygoteSocketAddress = - new LocalSocketAddress(ZYGOTE_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); + new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, + LocalSocketAddress.Namespace.RESERVED); mZygoteSecondarySocketAddress = - new LocalSocketAddress(ZYGOTE_SECONDARY_SOCKET_NAME, + new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSocketAddress = - new LocalSocketAddress(BLASTULA_POOL_SOCKET_NAME, + new LocalSocketAddress(Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); mBlastulaPoolSecondarySocketAddress = - new LocalSocketAddress(BLASTULA_POOL_SECONDARY_SOCKET_NAME, + new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); + + // TODO (chriswailes): Uncomment when the blastula pool can be enabled. +// fetchBlastulaPoolEnabledProp(); } public ZygoteProcess(LocalSocketAddress primarySocketAddress, @@ -272,6 +263,15 @@ public class ZygoteProcess { private ZygoteState secondaryZygoteState; /** + * If the blastula pool should be created and used to start applications. + * + * Setting this value to false will disable the creation, maintenance, and use of the blastula + * pool. When the blastula pool is disabled the application lifecycle will be identical to + * previous versions of Android. + */ + private boolean mBlastulaPoolEnabled = false; + + /** * Start a new process. * * <p>If processes are enabled, a new process is created and the @@ -327,6 +327,14 @@ public class ZygoteProcess { @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { + if (fetchBlastulaPoolEnabledProp()) { + // TODO (chriswailes): Send the appropriate command to the zygotes + Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled); + + // This can't be enabled yet, but we do want to test this code path. + mBlastulaPoolEnabled = false; + } + try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, @@ -407,7 +415,7 @@ public class ZygoteProcess { Process.ProcessStartResult result = new Process.ProcessStartResult(); // TODO (chriswailes): Move branch body into separate function. - if (useBlastulaPool && Zygote.BLASTULA_POOL_ENABLED && isValidBlastulaCommand(args)) { + if (useBlastulaPool && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { @@ -620,6 +628,7 @@ public class ZygoteProcess { final StringBuilder sb = new StringBuilder(); sb.append("--packages-for-uid="); + // TODO (chriswailes): Replace with String.join for (int i = 0; i < packagesForUid.length; ++i) { if (i != 0) { sb.append(','); @@ -656,11 +665,42 @@ public class ZygoteProcess { synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), - useBlastulaPool, + useBlastulaPool && mBlastulaPoolEnabled, argsForZygote); } } + private boolean fetchBlastulaPoolEnabledProp() { + boolean origVal = mBlastulaPoolEnabled; + + final String propertyString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, + BLASTULA_POOL_ENABLED_DEFAULT); + + if (!propertyString.isEmpty()) { + mBlastulaPoolEnabled = + Zygote.getSystemPropertyBoolean( + DeviceConfig.RuntimeNative.BLASTULA_POOL_ENABLED, + Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT)); + } + + return origVal != mBlastulaPoolEnabled; + } + + private long mLastPropCheckTimestamp = 0; + + private boolean fetchBlastulaPoolEnabledPropWithMinInterval() { + final long currentTimestamp = SystemClock.elapsedRealtime(); + + if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) { + mLastPropCheckTimestamp = currentTimestamp; + return fetchBlastulaPoolEnabledProp(); + } + + return false; + } + /** * Closes the connections to the zygote, if they exist. */ @@ -940,7 +980,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. - * @param socketName The name of the socket to connect to. + * @param zygoteSocketName The name of the socket to connect to. */ public static void waitForConnectionToZygote(String zygoteSocketName) { final LocalSocketAddress zygoteSocketAddress = @@ -950,7 +990,7 @@ public class ZygoteProcess { /** * Try connecting to the Zygote over and over again until we hit a time-out. - * @param address The name of the socket to connect to. + * @param zygoteSocketAddress The name of the socket to connect to. */ public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) { int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS; diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java index 2593c85cff12..c99ecb32d418 100644 --- a/core/java/android/os/connectivity/CellularBatteryStats.java +++ b/core/java/android/os/connectivity/CellularBatteryStats.java @@ -44,6 +44,7 @@ public final class CellularBatteryStats implements Parcelable { private long[] mTimeInRatMs; private long[] mTimeInRxSignalStrengthLevelMs; private long[] mTxTimeMs; + private long mMonitoredRailChargeConsumedMaMs; public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new Parcelable.Creator<CellularBatteryStats>() { @@ -74,6 +75,7 @@ public final class CellularBatteryStats implements Parcelable { out.writeLongArray(mTimeInRatMs); out.writeLongArray(mTimeInRxSignalStrengthLevelMs); out.writeLongArray(mTxTimeMs); + out.writeLong(mMonitoredRailChargeConsumedMaMs); } public void readFromParcel(Parcel in) { @@ -90,6 +92,7 @@ public final class CellularBatteryStats implements Parcelable { in.readLongArray(mTimeInRatMs); in.readLongArray(mTimeInRxSignalStrengthLevelMs); in.readLongArray(mTxTimeMs); + mMonitoredRailChargeConsumedMaMs = in.readLong(); } public long getLoggingDurationMs() { @@ -144,6 +147,10 @@ public final class CellularBatteryStats implements Parcelable { return mTxTimeMs; } + public long getMonitoredRailChargeConsumedMaMs() { + return mMonitoredRailChargeConsumedMaMs; + } + public void setLoggingDurationMs(long t) { mLoggingDurationMs = t; return; @@ -211,6 +218,11 @@ public final class CellularBatteryStats implements Parcelable { return; } + public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) { + mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs; + return; + } + public int describeContents() { return 0; } @@ -237,6 +249,7 @@ public final class CellularBatteryStats implements Parcelable { Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0); mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS]; Arrays.fill(mTxTimeMs, 0); + mMonitoredRailChargeConsumedMaMs = 0; return; } }
\ No newline at end of file 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/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 43c906495cb6..90a5f7660e0d 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -38,6 +38,7 @@ import android.content.pm.IPackageMoveObserver; import android.content.pm.PackageManager; import android.content.res.ObbInfo; import android.content.res.ObbScanner; +import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; @@ -55,6 +56,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemProperties; +import android.provider.MediaStore; import android.provider.Settings; import android.sysprop.VoldProperties; import android.system.ErrnoException; @@ -1096,12 +1098,32 @@ public class StorageManager { } /** - * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. + * Return the {@link StorageVolume} that contains the given file, or + * {@code null} if none. */ public @Nullable StorageVolume getStorageVolume(File file) { return getStorageVolume(getVolumeList(), file); } + /** + * Return the {@link StorageVolume} that contains the given + * {@link MediaStore} item. + */ + public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) { + final String volumeName = MediaStore.getVolumeName(uri); + switch (volumeName) { + case MediaStore.VOLUME_EXTERNAL: + return getPrimaryStorageVolume(); + default: + for (StorageVolume vol : getStorageVolumes()) { + if (Objects.equals(vol.getNormalizedUuid(), volumeName)) { + return vol; + } + } + } + throw new IllegalStateException("Unknown volume for " + uri); + } + /** {@hide} */ public static @Nullable StorageVolume getStorageVolume(File file, int userId) { return getStorageVolume(getVolumeList(userId, 0), file); @@ -1539,7 +1561,13 @@ public class StorageManager { return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false); } - /** {@hide} */ + /** + * Return if the currently booted device has the "isolated storage" feature + * flag enabled. This will eventually be fully enabled in the final + * {@link android.os.Build.VERSION_CODES#Q} release. + * + * @hide + */ @SystemApi @TestApi public static boolean hasIsolatedStorage() { 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..f6a8388a7d40 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -145,6 +145,38 @@ public final class DeviceConfig { @SystemApi public interface RuntimeNative { String NAMESPACE = "runtime_native"; + + /** + * Zygote flags. See {@link com.internal.os.Zygote}. + */ + + /** + * If {@code true}, enables the blastula pool feature. + * + * @hide for internal use only + */ + String BLASTULA_POOL_ENABLED = "blastula_pool_enabled"; + + /** + * The maximum number of processes to keep in the blastula pool. + * + * @hide for internal use only + */ + String BLASTULA_POOL_SIZE_MAX = "blastula_pool_size_max"; + + /** + * The minimum number of processes to keep in the blastula pool. + * + * @hide for internal use only + */ + String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max"; + + /** + * The threshold used to determine if the pool should be refilled. + * + * @hide for internal use only + */ + String BLASTULA_POOL_REFILL_THRESHOLD = "blastula_refill_threshold"; } /** @@ -316,10 +348,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/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 5f1c56016fa7..d28296786759 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -853,10 +853,8 @@ public final class DocumentsContract { private static final String PATH_DOCUMENT = "document"; private static final String PATH_CHILDREN = "children"; private static final String PATH_SEARCH = "search"; - // TODO(b/72055774): make private again once ScopedAccessProvider is refactored - /** {@hide} */ @UnsupportedAppUsage - public static final String PATH_TREE = "tree"; + private static final String PATH_TREE = "tree"; private static final String PARAM_QUERY = "query"; private static final String PARAM_MANAGE = "manage"; @@ -1283,8 +1281,9 @@ public final class DocumentsContract { * @see DocumentsProvider#openDocumentThumbnail(String, Point, * android.os.CancellationSignal) */ - public static Bitmap getDocumentThumbnail(ContentInterface content, Uri documentUri, Point size, - CancellationSignal signal) throws FileNotFoundException { + public static @Nullable Bitmap getDocumentThumbnail(@NonNull ContentResolver content, + @NonNull Uri documentUri, @NonNull Point size, @Nullable CancellationSignal signal) + throws FileNotFoundException { try { return ContentResolver.loadThumbnail(content, documentUri, Point.convert(size), signal, ImageDecoder.ALLOCATOR_SOFTWARE); @@ -1297,12 +1296,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size, - CancellationSignal signal) throws FileNotFoundException { - return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal); - } - /** * Create a new document with given MIME type and display name. * @@ -1311,8 +1304,9 @@ public final class DocumentsContract { * @param displayName name of new document * @return newly created document, or {@code null} if failed */ - public static Uri createDocument(ContentInterface content, Uri parentDocumentUri, - String mimeType, String displayName) throws FileNotFoundException { + public static @Nullable Uri createDocument(@NonNull ContentResolver content, + @NonNull Uri parentDocumentUri, @NonNull String mimeType, @NonNull String displayName) + throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri); @@ -1329,12 +1323,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Uri createDocument(ContentResolver content, Uri parentDocumentUri, - String mimeType, String displayName) throws FileNotFoundException { - return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName); - } - /** * Test if a document is descendant (child, grandchild, etc) from the given * parent. @@ -1344,7 +1332,7 @@ public final class DocumentsContract { * @return if given document is a descendant of the given parent. * @see Root#FLAG_SUPPORTS_IS_CHILD */ - public static boolean isChildDocument(@NonNull ContentInterface content, + public static boolean isChildDocument(@NonNull ContentResolver content, @NonNull Uri parentDocumentUri, @NonNull Uri childDocumentUri) throws FileNotFoundException { Preconditions.checkNotNull(content, "content can not be null"); @@ -1371,12 +1359,6 @@ public final class DocumentsContract { } } - @Deprecated - public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri, - Uri childDocumentUri) throws FileNotFoundException { - return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri); - } - /** * Change the display name of an existing document. * <p> @@ -1390,8 +1372,8 @@ public final class DocumentsContract { * @return the existing or new document after the rename, or {@code null} if * failed. */ - public static Uri renameDocument(ContentInterface content, Uri documentUri, - String displayName) throws FileNotFoundException { + public static @Nullable Uri renameDocument(@NonNull ContentResolver content, + @NonNull Uri documentUri, @NonNull String displayName) throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, documentUri); @@ -1408,19 +1390,13 @@ public final class DocumentsContract { } } - @Deprecated - public static Uri renameDocument(ContentResolver content, Uri documentUri, - String displayName) throws FileNotFoundException { - return renameDocument((ContentInterface) content, documentUri, displayName); - } - /** * Delete the given document. * * @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE} * @return if the document was deleted successfully. */ - public static boolean deleteDocument(ContentInterface content, Uri documentUri) + public static boolean deleteDocument(@NonNull ContentResolver content, @NonNull Uri documentUri) throws FileNotFoundException { try { final Bundle in = new Bundle(); @@ -1436,12 +1412,6 @@ public final class DocumentsContract { } } - @Deprecated - public static boolean deleteDocument(ContentResolver content, Uri documentUri) - throws FileNotFoundException { - return deleteDocument((ContentInterface) content, documentUri); - } - /** * Copies the given document. * @@ -1450,8 +1420,9 @@ public final class DocumentsContract { * document's copy. * @return the copied document, or {@code null} if failed. */ - public static Uri copyDocument(ContentInterface content, Uri sourceDocumentUri, - Uri targetParentDocumentUri) throws FileNotFoundException { + public static @Nullable Uri copyDocument(@NonNull ContentResolver content, + @NonNull Uri sourceDocumentUri, @NonNull Uri targetParentDocumentUri) + throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri); @@ -1467,12 +1438,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri, - Uri targetParentDocumentUri) throws FileNotFoundException { - return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri); - } - /** * Moves the given document under a new parent. * @@ -1482,8 +1447,9 @@ public final class DocumentsContract { * document. * @return the moved document, or {@code null} if failed. */ - public static Uri moveDocument(ContentInterface content, Uri sourceDocumentUri, - Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException { + public static @Nullable Uri moveDocument(@NonNull ContentResolver content, + @NonNull Uri sourceDocumentUri, @NonNull Uri sourceParentDocumentUri, + @NonNull Uri targetParentDocumentUri) throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri); @@ -1500,13 +1466,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri, - Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException { - return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri, - targetParentDocumentUri); - } - /** * Removes the given document from a parent directory. * @@ -1517,8 +1476,8 @@ public final class DocumentsContract { * @param parentDocumentUri parent document of the document to remove. * @return true if the document was removed successfully. */ - public static boolean removeDocument(ContentInterface content, Uri documentUri, - Uri parentDocumentUri) throws FileNotFoundException { + public static boolean removeDocument(@NonNull ContentResolver content, @NonNull Uri documentUri, + @NonNull Uri parentDocumentUri) throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, documentUri); @@ -1534,34 +1493,23 @@ public final class DocumentsContract { } } - @Deprecated - public static boolean removeDocument(ContentResolver content, Uri documentUri, - Uri parentDocumentUri) throws FileNotFoundException { - return removeDocument((ContentInterface) content, documentUri, parentDocumentUri); - } - /** * Ejects the given root. It throws {@link IllegalStateException} when ejection failed. * * @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected */ - public static void ejectRoot(ContentInterface content, Uri rootUri) { + public static void ejectRoot(@NonNull ContentResolver content, @NonNull Uri rootUri) { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, rootUri); content.call(rootUri.getAuthority(), METHOD_EJECT_ROOT, null, in); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); + } catch (Exception e) { + Log.w(TAG, "Failed to eject", e); } } - @Deprecated - public static void ejectRoot(ContentResolver content, Uri rootUri) { - ejectRoot((ContentInterface) content, rootUri); - } - /** * Returns metadata associated with the document. The type of metadata returned * is specific to the document type. For example the data returned for an image @@ -1592,7 +1540,7 @@ public final class DocumentsContract { * @param documentUri a Document URI * @return a Bundle of Bundles. */ - public static @Nullable Bundle getDocumentMetadata(@NonNull ContentInterface content, + public static @Nullable Bundle getDocumentMetadata(@NonNull ContentResolver content, @NonNull Uri documentUri) throws FileNotFoundException { Preconditions.checkNotNull(content, "content can not be null"); Preconditions.checkNotNull(documentUri, "documentUri can not be null"); @@ -1609,12 +1557,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri) - throws FileNotFoundException { - return getDocumentMetadata((ContentInterface) content, documentUri); - } - /** * Finds the canonical path from the top of the document tree. * @@ -1628,8 +1570,8 @@ public final class DocumentsContract { * @return the path of the document, or {@code null} if failed. * @see DocumentsProvider#findDocumentPath(String, String) */ - public static Path findDocumentPath(ContentInterface content, Uri treeUri) - throws FileNotFoundException { + public static @Nullable Path findDocumentPath(@NonNull ContentResolver content, + @NonNull Uri treeUri) throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, treeUri); @@ -1644,12 +1586,6 @@ public final class DocumentsContract { } } - @Deprecated - public static Path findDocumentPath(ContentResolver content, Uri treeUri) - throws FileNotFoundException { - return findDocumentPath((ContentInterface) content, treeUri); - } - /** * Creates an intent for obtaining a web link for the specified document. * @@ -1701,8 +1637,8 @@ public final class DocumentsContract { * @see DocumentsProvider#createWebLinkIntent(String, Bundle) * @see Intent#EXTRA_EMAIL */ - public static IntentSender createWebLinkIntent(ContentInterface content, Uri uri, - Bundle options) throws FileNotFoundException { + public static @Nullable IntentSender createWebLinkIntent(@NonNull ContentResolver content, + @NonNull Uri uri, @Nullable Bundle options) throws FileNotFoundException { try { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, uri); @@ -1723,12 +1659,6 @@ public final class DocumentsContract { } } - @Deprecated - public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri, - Bundle options) throws FileNotFoundException { - return createWebLinkIntent((ContentInterface) content, uri, options); - } - /** * Open the given image for thumbnail purposes, using any embedded EXIF * thumbnail if available, and providing orientation hints from the parent diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 0b3842080a75..643307eb89ae 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -17,6 +17,8 @@ package android.provider; import android.annotation.BytesLong; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.CurrentTimeSecondsLong; import android.annotation.DurationMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; @@ -40,7 +42,9 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.ImageDecoder; import android.graphics.Point; +import android.graphics.PostProcessor; import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; @@ -78,8 +82,15 @@ import java.util.Set; import java.util.regex.Pattern; /** - * The Media provider contains meta data for all available media on both internal - * and external storage devices. + * The contract between the media provider and applications. Contains + * definitions for the supported URIs and columns. + * <p> + * The media provider provides an indexed collection of common media types, such + * as {@link Audio}, {@link Video}, and {@link Images}, from any attached + * storage devices. Each collection is organized based on the primary MIME type + * of the underlying content; for example, {@code image/*} content is indexed + * under {@link Images}. The {@link Files} collection provides a broad view + * across all collections, and does not filter by MIME type. */ public final class MediaStore { private final static String TAG = "MediaStore"; @@ -847,18 +858,16 @@ public final class MediaStore { } /** - * Common fields for most MediaProvider tables + * Common media metadata columns. */ public interface MediaColumns extends BaseColumns { /** - * Path to the file on disk. + * Path to the media item 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. - * <p> - * Type: TEXT * * @deprecated Apps may not have filesystem permissions to directly * access this path. Instead of trying to open this path @@ -869,10 +878,11 @@ 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"; /** - * Hash of the file on disk. + * Hash of the media item on disk. * <p> * Contains a 20-byte binary blob which is the SHA-1 hash of the file as * persisted on disk. For performance reasons, the hash may not be @@ -883,44 +893,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> + * The size of the media item. */ + @BytesLong + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String SIZE = "_size"; /** - * The display name of the file - * <P>Type: TEXT</P> + * The display name of the media item. */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String DISPLAY_NAME = "_display_name"; /** - * The title of the content - * <P>Type: TEXT</P> + * The title of the media item. */ + @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> + * The time the media item was first added. */ + @CurrentTimeSecondsLong + @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> + * The time the media item was last modified. */ + @CurrentTimeSecondsLong + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_MODIFIED = "date_modified"; /** @@ -938,9 +948,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 +957,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 +993,56 @@ 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 + * The time the media item should be considered expired. Typically only + * meaningful in the context of {@link #IS_PENDING} or + * {@link #IS_TRASHED}. + * * @removed */ @Deprecated + @CurrentTimeSecondsLong + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String DATE_EXPIRES = "date_expires"; /** - * The width of the image/video in pixels. + * The width of the media item, in pixels. */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String WIDTH = "width"; /** - * The height of the image/video in pixels. + * The height of the media item, 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 +1053,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 +1065,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 +1078,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"; } @@ -1166,49 +1164,62 @@ public final class MediaStore { } /** - * Fields for master table for all media files. - * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED. + * File metadata columns. */ 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> + * The MIME type of the media item. + * <p> + * This is typically defined based on the file extension of the media + * item. However, it may be the value of the {@code format} attribute + * defined by the <em>Dublin Core Media Initiative</em> standard, + * extracted from any XMP metadata contained within this media item. + * <p class="note"> + * Note: the {@code format} attribute may be ignored if the top-level + * MIME type disagrees with the file extension. For example, it's + * reasonable for an {@code image/jpeg} file to declare a {@code format} + * of {@code image/vnd.google.panorama360+jpg}, but declaring a + * {@code format} of {@code audio/ogg} would be ignored. + * <p> + * This is a read-only column that is automatically computed. */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String MIME_TYPE = "mime_type"; /** - * The title of the content - * <P>Type: TEXT</P> + * The title of the media item. */ + @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 +1252,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"; } } @@ -1256,27 +1268,26 @@ public final class MediaStore { public static final Point MICRO_SIZE = new Point(96, 96); } - /** Column fields for downloaded files used in {@link Downloads} table */ + /** + * Download metadata columns. + */ public interface DownloadColumns extends MediaColumns { /** - * Uri indicating where the file has been downloaded from. - * <p> - * Type: TEXT + * Uri indicating where the item has been downloaded from. */ + @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"; } @@ -1436,35 +1447,37 @@ public final class MediaStore { } /** - * Contains meta data for all available images. + * Collection of all media with MIME type of {@code image/*}. */ public static final class Images { + /** + * Image metadata columns. + */ 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 +1485,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 +1497,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> + * The time the media item was taken. */ + @CurrentTimeMillisLong + @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 +1538,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,23 +1553,40 @@ 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"; } public static final class Media implements ImageColumns { + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy) { return cr.query(uri, projection, where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor query(ContentResolver cr, Uri uri, String[] projection, String selection, String [] selectionArgs, String orderBy) { return cr.query(uri, projection, selection, @@ -1569,9 +1598,12 @@ public final class MediaStore { * * @param cr The content resolver to use * @param url The url of the image - * @throws FileNotFoundException - * @throws IOException + * @deprecated loading of images should be performed through + * {@link ImageDecoder#createSource(ContentResolver, Uri)}, + * which offers modern features like + * {@link PostProcessor}. */ + @Deprecated public static final Bitmap getBitmap(ContentResolver cr, Uri url) throws FileNotFoundException, IOException { InputStream input = cr.openInputStream(url); @@ -1588,8 +1620,11 @@ public final class MediaStore { * @param name The name of the image * @param description The description of the image * @return The URL to the newly created image - * @throws FileNotFoundException + * @deprecated inserting of images should be performed through + * {@link MediaStore#createPending(Context, PendingParams)}, + * which offers richer control over lifecycle. */ + @Deprecated public static final String insertImage(ContentResolver cr, String imagePath, String name, String description) throws FileNotFoundException { // Check if file exists with a FileInputStream @@ -1616,7 +1651,11 @@ public final class MediaStore { * @param description The description of the image * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored * for any reason. + * @deprecated inserting of images should be performed through + * {@link MediaStore#createPending(Context, PendingParams)}, + * which offers richer control over lifecycle. */ + @Deprecated public static final String insertImage(ContentResolver cr, Bitmap source, String title, String description) { ContentValues values = new ContentValues(); @@ -1711,15 +1750,33 @@ public final class MediaStore { */ @Deprecated public static class Thumbnails implements BaseColumns { + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection) { return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER); } + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection) { return cr.query(EXTERNAL_CONTENT_URI, projection, @@ -1848,8 +1905,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,128 +1915,137 @@ 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"; } } /** - * Container for all audio content. + * Collection of all media with MIME type of {@code audio/*}. */ public static final class Audio { /** - * Columns for audio file that show up in multiple tables. + * Audio metadata columns. */ public interface AudioColumns extends MediaColumns { /** * 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> + * The duration of the audio item. */ + @DurationMillisLong + @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> + * The position within the audio item at which playback should be + * resumed. */ + @DurationMillisLong + @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 +2054,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 +2118,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 +2207,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))); } @@ -2197,13 +2263,13 @@ public final class MediaStore { } /** - * Columns representing an audio genre + * Audio genre metadata columns. */ public interface GenresColumns { /** * The name of the genre - * <P>Type: TEXT</P> */ + @Column(Cursor.FIELD_TYPE_STRING) public static final String NAME = "name"; } @@ -2287,26 +2353,26 @@ 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"; } } /** - * Columns representing a playlist + * Audio playlist metadata columns. */ 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 +2383,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 +2393,21 @@ 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> + * The time the media item was first added. */ + @CurrentTimeSecondsLong + @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> + * The time the media item was last modified. */ + @CurrentTimeSecondsLong + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) public static final String DATE_MODIFIED = "date_modified"; } @@ -2426,6 +2490,7 @@ public final class MediaStore { /** * The ID within the playlist. */ + @Column(Cursor.FIELD_TYPE_INTEGER) public static final String _ID = "_id"; /** @@ -2436,20 +2501,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"; /** @@ -2460,30 +2525,32 @@ public final class MediaStore { } /** - * Columns representing an artist + * Audio artist metadata columns. */ 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"; } @@ -2545,40 +2612,40 @@ public final class MediaStore { } /** - * Columns representing an album + * Audio album metadata columns. */ public interface AlbumColumns { /** * 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 +2653,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 +2662,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 +2685,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,8 +2743,48 @@ 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"; + } } + /** + * Collection of all media with MIME type of {@code video/*}. + */ public static final class Video { /** @@ -2685,69 +2792,78 @@ public final class MediaStore { */ public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME; + /** + * @deprecated all queries should be performed through + * {@link ContentResolver} directly, which offers modern + * features like {@link CancellationSignal}. + */ + @Deprecated public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } + /** + * Video metadata columns. + */ public interface VideoColumns extends MediaColumns { /** - * The duration of the video file, in ms - * <P>Type: INTEGER (long)</P> + * The duration of the video item. */ + @DurationMillisLong + @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 +2871,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 +2883,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> + * The time the media item was taken. */ + @CurrentTimeMillisLong + @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 +2917,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,39 +2932,37 @@ 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"; /** - * The bookmark for the video. Time in ms. Represents the location in the video that the - * 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> + * The position within the video item at which playback should be + * resumed. */ + @DurationMillisLong + @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 +3129,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 +3139,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 +3160,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 d75c5e28e3f6..a465b3271407 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 /** @@ -6103,7 +6127,7 @@ public final class Settings { * Indicates which clock face to show on lock screen and AOD while docked. * @hide */ - private static final String DOCKED_CLOCK_FACE = "docked_clock_face"; + public static final String DOCKED_CLOCK_FACE = "docked_clock_face"; /** * Set by the system to track if the user needs to see the call to action for @@ -11707,6 +11731,14 @@ public final class Settings { public static final String APP_IDLE_CONSTANTS = "app_idle_constants"; /** + * Enable ART bytecode verification verifications for debuggable apps. + * 0 = disable, 1 = enable. + * @hide + */ + public static final String ART_VERIFIER_VERIFY_DEBUGGABLE = + "art_verifier_verify_debuggable"; + + /** * Power manager specific settings. * This is encoded as a key=value list, separated by commas. Ex: * @@ -12369,6 +12401,14 @@ public final class Settings { public static final String GAME_DRIVER_WHITELIST = "game_driver_whitelist"; /** + * List of libraries in sphal accessible by Game Driver + * The string is a list of library names, separated by colon. + * i.e. <lib1>:<lib2>:...:<libN> + * @hide + */ + public static final String GAME_DRIVER_SPHAL_LIBRARIES = "game_driver_sphal_libraries"; + + /** * Ordered GPU debug layer list for Vulkan * i.e. <layer1>:<layer2>:...:<layerN> * @hide diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 81d066d3d656..ce83a57bf68b 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(); @@ -128,6 +126,14 @@ public abstract class AugmentedAutofillService extends Service { } /** + * Called when the Android system connects to service. + * + * <p>You should generally do initialization here rather than in {@link #onCreate}. + */ + public void onConnected() { + } + + /** * Asks the service to handle an "augmented" autofill request. * * <p>This method is called when the "stantard" autofill service cannot handle a request, which @@ -160,6 +166,14 @@ public abstract class AugmentedAutofillService extends Service { @NonNull FillCallback callback) { } + /** + * Called when the Android system disconnects from the service. + * + * <p> At this point this service may no longer be an active {@link AugmentedAutofillService}. + */ + public void onDisconnected() { + } + private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId, @NonNull ComponentName componentName, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, long requestTime, 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..d361a2c50935 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. * @@ -263,6 +284,24 @@ public abstract class ContentCaptureService extends Service { } /** + * Disables the Content Capture service for the given user. + */ + public final void disableContentCaptureServices() { + if (DEBUG) Log.d(TAG, "disableContentCaptureServices()"); + + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "disableContentCaptureServices(): no server callback"); + return; + } + try { + callback.disableSelf(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * Called when the Android system disconnects from the service. * * <p> At this point this service may no longer be an active {@link AutofillService}. @@ -339,7 +378,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 +422,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/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl index 2a729b6dc874..8bc8defede80 100644 --- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl @@ -17,7 +17,6 @@ package android.service.contentcapture; import android.content.ComponentName; -import com.android.internal.os.IResultReceiver; import java.util.List; @@ -28,4 +27,5 @@ import java.util.List; */ oneway interface IContentCaptureServiceCallback { void setContentCaptureWhitelist(in List<String> packages, in List<ComponentName> activities); -} + void disableSelf(); + } diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index ffb524d5c2e4..a46d04765e2c 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -76,6 +76,11 @@ import java.util.concurrent.atomic.AtomicInteger; * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero * priority. * + * <p>Old implementations of EuiccService may support passing in slot IDs equal to + * {@link android.telephony.SubscriptionManager#INVALID_SIM_SLOT_INDEX}, which allows the LPA to + * decide which eUICC to target when there are multiple eUICCs. This behavior is not supported in + * Android Q or later. + * * @hide */ @SystemApi @@ -546,7 +551,7 @@ public abstract class EuiccService extends Service { int resultCode = EuiccService.this.onDownloadSubscription( slotId, subscription, switchAfterDownload, forceDeactivateSim); result = new DownloadSubscriptionResult(resultCode, - 0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID); + 0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID); } try { callback.onComplete(result); diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 93189b310485..bddc5ef3dd2d 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -15,13 +15,22 @@ */ package android.service.notification; +import android.annotation.SystemApi; import android.app.Notification; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; /** * Ranking updates from the Assistant. + * + * The updates are provides as a {@link Bundle} of signals, using the keys provided in this + * class. + * Each {@code KEY} specifies what type of data it supports and what kind of Adjustment it + * realizes on the notification rankings. + * + * Notifications affected by the Adjustment will be re-ranked if necessary. */ public final class Adjustment implements Parcelable { private final String mPackage; @@ -36,13 +45,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"; /** @@ -102,7 +111,9 @@ public final class Adjustment implements Parcelable { * @param signals A bundle of signals that should inform notification display, ordering, and * interruptiveness. * @param explanation A human-readable justification for the adjustment. + * @hide */ + @SystemApi public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) { mPackage = pkg; mKey = key; @@ -111,7 +122,30 @@ public final class Adjustment implements Parcelable { mUser = user; } - private Adjustment(Parcel in) { + /** + * Create a notification adjustment. + * + * @param pkg The package of the notification. + * @param key The notification key. + * @param signals A bundle of signals that should inform notification display, ordering, and + * interruptiveness. + * @param explanation A human-readable justification for the adjustment. + * @param userHandle User handle for for whose the adjustments will be applied. + */ + public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, + UserHandle userHandle) { + mPackage = pkg; + mKey = key; + mSignals = signals; + mExplanation = explanation; + mUser = userHandle.getIdentifier(); + } + + /** + * @hide + */ + @SystemApi + protected Adjustment(Parcel in) { if (in.readInt() == 1) { mPackage = in.readString(); } else { @@ -159,10 +193,16 @@ public final class Adjustment implements Parcelable { return mSignals; } + /** @hide */ + @SystemApi public int getUser() { return mUser; } + public UserHandle getUserHandle() { + return UserHandle.of(mUser); + } + @Override public int describeContents() { return 0; 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/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index be772af529ef..575401458691 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -77,6 +77,13 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { */ public static final int ALIGN_BASELINE = 1; + /** + * A constant indicating that this span should be vertically centered between + * the top and the lowest descender. + * @hide + */ + public static final int ALIGN_CENTER = 2; + protected final int mVerticalAlignment; @UnsupportedAppUsage @@ -142,6 +149,8 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { int transY = bottom - b.getBounds().bottom; if (mVerticalAlignment == ALIGN_BASELINE) { transY -= paint.getFontMetricsInt().descent; + } else if (mVerticalAlignment == ALIGN_CENTER) { + transY = (bottom - top) / 2 - b.getBounds().height() / 2; } canvas.translate(x, transY); 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/util/StatsLog.java b/core/java/android/util/StatsLog.java index ace4bf477af6..29ced3eda4ec 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -16,7 +16,14 @@ package android.util; +import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.PACKAGE_USAGE_STATS; + +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.app.IActivityManager; +import android.content.Context; import android.os.IStatsManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -31,7 +38,10 @@ public final class StatsLog extends StatsLogInternal { private static IStatsManager sService; - private StatsLog() {} + private static Object sLogLock = new Object(); + + private StatsLog() { + } /** * Logs a start event. @@ -40,11 +50,13 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logStart(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging start"); + } return false; } service.sendAppBreadcrumbAtom(label, @@ -52,7 +64,9 @@ public final class StatsLog extends StatsLogInternal { return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging start"); + } return false; } } @@ -65,18 +79,22 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logStop(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging stop"); + } return false; } service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging stop"); + } return false; } } @@ -89,11 +107,13 @@ public final class StatsLog extends StatsLogInternal { * @return True if the log request was sent to statsd. */ public static boolean logEvent(int label) { - synchronized (StatsLog.class) { + synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); if (service == null) { - if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event"); + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging event"); + } return false; } service.sendAppBreadcrumbAtom( @@ -101,7 +121,51 @@ public final class StatsLog extends StatsLogInternal { return true; } catch (RemoteException e) { sService = null; - if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event"); + if (DEBUG) { + Slog.d(TAG, "Failed to connect to statsd when logging event"); + } + return false; + } + } + } + + /** + * Logs an event for binary push for module updates. + * + * @param trainName name of install train. + * @param trainVersionCode version code of the train. + * @param options optional flags about this install. + * @param state current install state. + * @param experimentIds experiment ids. + * @return True if the log request was sent to statsd. + */ + @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) + public static boolean logBinaryPushStateChanged(@NonNull String trainName, + long trainVersionCode, int options, int state, + @NonNull long[] experimentIds) { + synchronized (sLogLock) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when logging event"); + } + return false; + } + int userId = IActivityManager.Stub.asInterface( + ServiceManager.getService("activity")) + .getCurrentUser() + .id; + service.sendBinaryPushStateChangedAtom( + trainName, trainVersionCode, options, state, experimentIds); + return true; + } catch (RemoteException e) { + sService = null; + if (DEBUG) { + Slog.d(TAG, + "Failed to connect to StatsCompanionService when logging " + + "BinaryPushStateChanged"); + } return false; } } @@ -118,7 +182,7 @@ public final class StatsLog extends StatsLogInternal { /** * Add a log to the stats log. * - * @param id The id of the atom + * @param id The id of the atom * @param params The parameters of the atom's message. */ public static void write(int id, @NonNull Object... params) { @@ -128,4 +192,13 @@ public final class StatsLog extends StatsLogInternal { (boolean) params[4], (int) params[5]); } } + + private static void enforceDumpCallingPermission(Context context) { + context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission."); + } + + private static void enforcesageStatsCallingPermission(Context context) { + context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, + "Need PACKAGE_USAGE_STATS permission."); + } } 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/Display.java b/core/java/android/view/Display.java index 49bae280321e..cb5100a4d57a 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -76,7 +76,7 @@ public final class Display { private final int mLayerStack; private final int mFlags; private final int mType; - private final String mAddress; + private final DisplayAddress mAddress; private final int mOwnerUid; private final String mOwnerPackageName; private final Resources mResources; @@ -557,7 +557,7 @@ public final class Display { * @hide */ @UnsupportedAppUsage - public String getAddress() { + public DisplayAddress getAddress() { return mAddress; } diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java new file mode 100644 index 000000000000..17ea4c423be5 --- /dev/null +++ b/core/java/android/view/DisplayAddress.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** Display identifier that is stable across reboots. + * + * @hide + */ +public abstract class DisplayAddress implements Parcelable { + /** + * Creates an address for a physical display given its stable ID. + * + * A physical display ID is stable if the display can be identified using EDID information. + * + * @param physicalDisplayId A physical display ID. + * @return The {@link Physical} address, or {@code null} if the ID is not stable. + * @see SurfaceControl#getPhysicalDisplayIds + */ + @Nullable + public static Physical fromPhysicalDisplayId(long physicalDisplayId) { + final Physical address = new Physical(physicalDisplayId); + return address.getModel() == 0 ? null : address; + } + + /** + * Creates an address for a network display given its MAC address. + * + * @param macAddress A MAC address in colon notation. + * @return The {@link Network} address. + */ + @NonNull + public static Network fromMacAddress(String macAddress) { + return new Network(macAddress); + } + + /** + * Address for a physically connected display. + * + * A {@link Physical} address is represented by a 64-bit identifier combining the port and model + * of a display. The port, located in the least significant byte, uniquely identifies a physical + * connector on the device for display output like eDP or HDMI. The model, located in the upper + * bits, uniquely identifies a display model across manufacturers by encoding EDID information. + */ + public static final class Physical extends DisplayAddress { + private static final int PHYSICAL_DISPLAY_ID_MODEL_SHIFT = 8; + private static final int PORT_MASK = 0xFF; + + private final long mPhysicalDisplayId; + + /** + * Physical port to which the display is connected. + */ + public byte getPort() { + return (byte) mPhysicalDisplayId; + } + + /** + * Model identifier unique across manufacturers. + */ + public long getModel() { + return mPhysicalDisplayId >>> PHYSICAL_DISPLAY_ID_MODEL_SHIFT; + } + + @Override + public boolean equals(Object other) { + return other instanceof Physical + && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId; + } + + @Override + public String toString() { + return new StringBuilder("{") + .append("port=").append(getPort() & PORT_MASK) + .append(", model=0x").append(Long.toHexString(getModel())) + .append("}") + .toString(); + } + + @Override + public int hashCode() { + return Long.hashCode(mPhysicalDisplayId); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mPhysicalDisplayId); + } + + private Physical(long physicalDisplayId) { + mPhysicalDisplayId = physicalDisplayId; + } + + public static final Parcelable.Creator<Physical> CREATOR = + new Parcelable.Creator<Physical>() { + @Override + public Physical createFromParcel(Parcel in) { + return new Physical(in.readLong()); + } + + @Override + public Physical[] newArray(int size) { + return new Physical[size]; + } + }; + } + + /** + * Address for a network-connected display. + */ + public static final class Network extends DisplayAddress { + private final String mMacAddress; + + @Override + public boolean equals(Object other) { + return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress); + } + + @Override + public String toString() { + return mMacAddress; + } + + @Override + public int hashCode() { + return mMacAddress.hashCode(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(mMacAddress); + } + + private Network(String macAddress) { + mMacAddress = macAddress; + } + + public static final Parcelable.Creator<Network> CREATOR = + new Parcelable.Creator<Network>() { + @Override + public Network createFromParcel(Parcel in) { + return new Network(in.readString()); + } + + @Override + public Network[] newArray(int size) { + return new Network[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index ad8fee978c9a..3aa779bc3df6 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -61,7 +61,7 @@ public final class DisplayInfo implements Parcelable { * Display address, or null if none. * Interpretation varies by display type. */ - public String address; + public DisplayAddress address; /** * The human-readable name of the display. @@ -385,7 +385,7 @@ public final class DisplayInfo implements Parcelable { layerStack = source.readInt(); flags = source.readInt(); type = source.readInt(); - address = source.readString(); + address = source.readParcelable(null); name = source.readString(); appWidth = source.readInt(); appHeight = source.readInt(); @@ -432,7 +432,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(layerStack); dest.writeInt(this.flags); dest.writeInt(type); - dest.writeString(address); + dest.writeParcelable(address, flags); dest.writeString(name); dest.writeInt(appWidth); dest.writeInt(appHeight); 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 83df33e9742e..2b440dc80cfd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -24,6 +24,7 @@ import static java.lang.Math.max; import android.animation.AnimatorInflater; import android.animation.StateListAnimator; +import android.annotation.AttrRes; import android.annotation.CallSuper; import android.annotation.ColorInt; import android.annotation.DrawableRes; @@ -5108,7 +5109,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private SparseIntArray mAttributeSourceResId; @Nullable - private int[] mAttributeResolutionStack; + private SparseArray<int[]> mAttributeResolutionStacks; @StyleRes private int mExplicitStyle; @@ -5963,11 +5964,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <b>Note:</b> this method will only return actual values if the view attribute debugging * is enabled in Android developer options. * + * @param attribute Attribute resource ID for which the resolution stack should be returned. * @return ordered list of resource ID that are considered when resolving attribute values for * this {@link View}. */ @NonNull - public List<Integer> getAttributeResolutionStack() { + public List<Integer> getAttributeResolutionStack(@AttrRes int attribute) { ArrayList<Integer> stack = new ArrayList<>(); if (!sDebugViewAttributes) { return stack; @@ -5975,8 +5977,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mSourceLayoutId != ID_NULL) { stack.add(mSourceLayoutId); } - for (int i = 0; i < mAttributeResolutionStack.length; i++) { - stack.add(mAttributeResolutionStack[i]); + int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute); + if (attributeResolutionStack == null) { + return stack; + } + for (int i = 0; i < attributeResolutionStack.length; i++) { + stack.add(attributeResolutionStack[i]); } return stack; } @@ -6143,9 +6149,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - mAttributeResolutionStack = context.getTheme().getAttributeResolutionStack( + int[] attributeResolutionStack = context.getTheme().getAttributeResolutionStack( defStyleAttr, defStyleRes, mExplicitStyle); + if (mAttributeResolutionStacks == null) { + mAttributeResolutionStacks = new SparseArray<>(); + } + if (mAttributeSourceResId == null) { mAttributeSourceResId = new SparseIntArray(); } @@ -6154,6 +6164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, for (int j = 0; j < indexCount; ++j) { final int index = t.getIndex(j); mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index, 0)); + mAttributeResolutionStacks.append(styleable[index], attributeResolutionStack); } } @@ -6456,6 +6467,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, arr.recycle(); } + private void initializeScrollBarDrawable() { + initScrollCache(); + + if (mScrollCache.scrollBar == null) { + mScrollCache.scrollBar = new ScrollBarDrawable(); + mScrollCache.scrollBar.setState(getDrawableState()); + mScrollCache.scrollBar.setCallback(this); + } + } + /** * <p> * Initializes the scrollbars from a given set of styled attributes. This @@ -6541,6 +6562,106 @@ public class View implements Drawable.Callback, KeyEvent.Callback, resolvePadding(); } + /** + * Defines the vertical scrollbar thumb drawable + * @attr ref android.R.styleable#View_scrollbarThumbVertical + * + * @see #awakenScrollBars(int) + * @see #isVerticalScrollBarEnabled() + * @see #setVerticalScrollBarEnabled(boolean) + */ + public void setVerticalScrollbarThumbDrawable(@Nullable Drawable drawable) { + initializeScrollBarDrawable(); + mScrollCache.scrollBar.setVerticalThumbDrawable(drawable); + } + + /** + * Defines the vertical scrollbar track drawable + * @attr ref android.R.styleable#View_scrollbarTrackVertical + * + * @see #awakenScrollBars(int) + * @see #isVerticalScrollBarEnabled() + * @see #setVerticalScrollBarEnabled(boolean) + */ + public void setVerticalScrollbarTrackDrawable(@Nullable Drawable drawable) { + initializeScrollBarDrawable(); + mScrollCache.scrollBar.setVerticalTrackDrawable(drawable); + } + + /** + * Defines the horizontal thumb drawable + * @attr ref android.R.styleable#View_scrollbarThumbHorizontal + * + * @see #awakenScrollBars(int) + * @see #isHorizontalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + */ + public void setHorizontalScrollbarThumbDrawable(@Nullable Drawable drawable) { + initializeScrollBarDrawable(); + mScrollCache.scrollBar.setHorizontalThumbDrawable(drawable); + } + + /** + * Defines the horizontal track drawable + * @attr ref android.R.styleable#View_scrollbarTrackHorizontal + * + * @see #awakenScrollBars(int) + * @see #isHorizontalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + */ + public void setHorizontalScrollbarTrackDrawable(@Nullable Drawable drawable) { + initializeScrollBarDrawable(); + mScrollCache.scrollBar.setHorizontalTrackDrawable(drawable); + } + + /** + * Returns the currently configured Drawable for the thumb of the vertical scroll bar if it + * exists, null otherwise. + * + * @see #awakenScrollBars(int) + * @see #isVerticalScrollBarEnabled() + * @see #setVerticalScrollBarEnabled(boolean) + */ + public @Nullable Drawable getVerticalScrollbarThumbDrawable() { + return mScrollCache != null ? mScrollCache.scrollBar.getVerticalThumbDrawable() : null; + } + + /** + * Returns the currently configured Drawable for the track of the vertical scroll bar if it + * exists, null otherwise. + * + * @see #awakenScrollBars(int) + * @see #isVerticalScrollBarEnabled() + * @see #setVerticalScrollBarEnabled(boolean) + */ + public @Nullable Drawable getVerticalScrollbarTrackDrawable() { + return mScrollCache != null ? mScrollCache.scrollBar.getVerticalTrackDrawable() : null; + } + + /** + * Returns the currently configured Drawable for the thumb of the horizontal scroll bar if it + * exists, null otherwise. + * + * @see #awakenScrollBars(int) + * @see #isHorizontalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + */ + public @Nullable Drawable getHorizontalScrollbarThumbDrawable() { + return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalThumbDrawable() : null; + } + + /** + * Returns the currently configured Drawable for the track of the horizontal scroll bar if it + * exists, null otherwise. + * + * @see #awakenScrollBars(int) + * @see #isHorizontalScrollBarEnabled() + * @see #setHorizontalScrollBarEnabled(boolean) + */ + public @Nullable Drawable getHorizontalScrollbarTrackDrawable() { + return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalTrackDrawable() : null; + } + private void initializeScrollIndicatorsInternal() { // Some day maybe we'll break this into top/left/start/etc. and let the // client control it. Until then, you can have any scroll indicator you @@ -9366,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/Window.java b/core/java/android/view/Window.java index 097f368ac2a8..3544a8733c68 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -622,11 +622,10 @@ public abstract class Window { /** @hide */ public interface WindowControllerCallback { /** - * Moves the activity from - * Moves the activity from {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing - * mode to {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. + * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing + * mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */ - void exitFreeformMode() throws RemoteException; + void toggleFreeformWindowingMode() throws RemoteException; /** * Puts the activity in picture-in-picture mode if the activity supports. 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..87e358c1165f 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 { @@ -260,39 +327,8 @@ public final class ContentCaptureManager { case RESULT_CODE_NOT_SERVICE: throw new SecurityException("caller is not user's ContentCapture service"); default: - throw new IllegalStateException("received invalid result: " + resultCode); - } - } - - /** - * Sets whether Content Capture is enabled for the given user. - * - * @throws SecurityException if caller is not the app that owns the Content Capture service - * associated with the user. - * - * @hide - */ - @SystemApi - @TestApi - public void setContentCaptureFeatureEnabled(boolean enabled) { - if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled); - - final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); - final int resultCode; - try { - mService.setContentCaptureFeatureEnabled(enabled, resultReceiver); - resultCode = resultReceiver.getIntResult(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - switch (resultCode) { - case RESULT_CODE_TRUE: - // Our work is done here, in our void existance... - return; - case RESULT_CODE_NOT_SERVICE: - throw new SecurityException("caller is not user's ContentCapture service"); - default: - throw new IllegalStateException("received invalid result: " + resultCode); + Log.wtf(TAG, "received invalid result: " + resultCode); + return false; } } @@ -314,22 +350,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/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 26cf34c1b88e..e3b0372a8cc7 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -67,9 +67,4 @@ oneway interface IContentCaptureManager { * Returns whether the content capture feature is enabled for the calling user. */ void isContentCaptureFeatureEnabled(in IResultReceiver result); - - /** - * Sets whether the content capture feature is enabled for the given user. - */ - void setContentCaptureFeatureEnabled(boolean enabled, in IResultReceiver result); } 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/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2171fc52a0ba..3555822b1f1f 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -760,7 +760,7 @@ public class WebView extends AbsoluteLayout * <p> * The {@code encoding} parameter specifies whether the data is base64 or URL * encoded. If the data is base64 encoded, the value of the encoding - * parameter must be 'base64'. HTML can be encoded with {@link + * parameter must be {@code "base64"}. HTML can be encoded with {@link * android.util.Base64#encodeToString(byte[],int)} like so: * <pre> * String unencodedHtml = @@ -768,11 +768,15 @@ public class WebView extends AbsoluteLayout * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING); * webView.loadData(encodedHtml, "text/html", "base64"); * </pre> - * <p> + * <p class="note"> * For all other values of {@code encoding} (including {@code null}) it is assumed that the * data uses ASCII encoding for octets inside the range of safe URL characters and use the * standard %xx hex encoding of URLs for octets outside that range. See <a * href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information. + * Applications targeting {@link android.os.Build.VERSION_CODES#Q} or later must either use + * base64 or encode any {@code #} characters in the content as {@code %23}, otherwise they + * will be treated as the end of the content and the remaining text used as a document + * fragment identifier. * <p> * The {@code mimeType} parameter specifies the format of the data. * If WebView can't handle the specified MIME type, it will download the data. @@ -820,7 +824,8 @@ public class WebView extends AbsoluteLayout * <p> * If the base URL uses the data scheme, this method is equivalent to * calling {@link #loadData(String,String,String) loadData()} and the - * historyUrl is ignored, and the data will be treated as part of a data: URL. + * historyUrl is ignored, and the data will be treated as part of a data: URL, + * including the requirement that the content be URL-encoded or base64 encoded. * If the base URL uses any other scheme, then the data will be loaded into * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded * entities in the string will not be decoded. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4b7c393b1b96..f01babe7b894 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -325,13 +325,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te /** * The current position of the selector in the list. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) int mSelectorPosition = INVALID_POSITION; /** * Defines the selector's location and dimension at drawing time */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) Rect mSelectorRect = new Rect(); /** 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/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index e91f87e7ec3d..1bed32ecf347 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -17,12 +17,15 @@ package android.widget; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.View; import com.android.internal.widget.ScrollBarUtils; @@ -36,7 +39,7 @@ import com.android.internal.widget.ScrollBarUtils; public class ScrollBarDrawable extends Drawable implements Drawable.Callback { private Drawable mVerticalTrack; private Drawable mHorizontalTrack; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768422) private Drawable mVerticalThumb; private Drawable mHorizontalThumb; @@ -226,7 +229,10 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback { } } - @UnsupportedAppUsage + /** + * @see android.view.View#setVerticalThumbDrawable(Drawable) + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void setVerticalThumbDrawable(Drawable thumb) { if (mVerticalThumb != null) { mVerticalThumb.setCallback(null); @@ -236,6 +242,37 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback { mVerticalThumb = thumb; } + /** + * @see View#getVerticalTrackDrawable() + */ + public @Nullable Drawable getVerticalTrackDrawable() { + return mVerticalTrack; + } + + /** + * @see View#getVerticalThumbDrawable() + */ + public @Nullable Drawable getVerticalThumbDrawable() { + return mVerticalThumb; + } + + /** + * @see View#getHorizontalTrackDrawable() + */ + public @Nullable Drawable getHorizontalTrackDrawable() { + return mHorizontalTrack; + } + + /** + * @see View#getHorizontalThumbDrawable() + */ + public @Nullable Drawable getHorizontalThumbDrawable() { + return mHorizontalThumb; + } + + /** + * @see android.view.View#setVerticalTrackDrawable(Drawable) + */ public void setVerticalTrackDrawable(Drawable track) { if (mVerticalTrack != null) { mVerticalTrack.setCallback(null); @@ -245,7 +282,10 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback { mVerticalTrack = track; } - @UnsupportedAppUsage + /** + * @see android.view.View#setHorizontalThumbDrawable(Drawable) + */ + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void setHorizontalThumbDrawable(Drawable thumb) { if (mHorizontalThumb != null) { mHorizontalThumb.setCallback(null); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 630c38a734bd..172f1d8a8f20 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -2078,6 +2078,11 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { return ic; } + @Override + public boolean checkInputConnectionProxy(View view) { + return view == mSearchView; + } + private void showSoftInputIfNecessary() { if (mHasPendingShowSoftInputRequest) { final InputMethodManager imm = 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..0fbd4dca700b 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; @@ -71,6 +72,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.DocumentsContract; +import android.provider.Downloads; import android.provider.OpenableColumns; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; @@ -486,6 +488,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: @@ -598,20 +621,39 @@ public class ChooserActivity extends ResolverActivity { } } + /** + * Wrapping the ContentResolver call to expose for easier mocking, + * and to avoid mocking Android core classes. + */ + @VisibleForTesting + public Cursor queryResolver(ContentResolver resolver, Uri uri) { + return resolver.query(uri, null, null, null, null); + } + private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) { String fileName = null; boolean hasThumbnail = false; - Cursor cursor = resolver.query(uri, null, null, null, null); - if (cursor != null && cursor.getCount() > 0) { - int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); - int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); - cursor.moveToFirst(); - fileName = cursor.getString(nameIndex); - if (flagsIndex != -1) { - hasThumbnail = (cursor.getInt(flagsIndex) - & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0; + try (Cursor cursor = queryResolver(resolver, uri)) { + if (cursor != null && cursor.getCount() > 0) { + int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE); + int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); + + cursor.moveToFirst(); + if (nameIndex != -1) { + fileName = cursor.getString(nameIndex); + } else if (titleIndex != -1) { + fileName = cursor.getString(titleIndex); + } + + if (flagsIndex != -1) { + hasThumbnail = (cursor.getInt(flagsIndex) + & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0; + } } + } catch (SecurityException e) { + Log.w(TAG, "Error loading file preview", e); } if (TextUtils.isEmpty(fileName)) { @@ -633,40 +675,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); } + } + } + + private void loadFileUriIntoView(Uri uri) { + FileInfo fileInfo = extractFileInfo(uri, getContentResolver()); - FileInfo fileInfo = extractFileInfo(uris.get(0), resolver); - int remFileCount = uris.size() - 1; - String fileName = getResources().getQuantityString(R.plurals.file_count, - remFileCount, fileInfo.name, remFileCount); + TextView fileNameView = findViewById(R.id.content_preview_filename); + fileNameView.setText(fileInfo.name); - fileNameView.setText(fileName); + 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/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index c4ab91f8b429..8a90cade35a2 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -44,9 +44,9 @@ interface IAppOpsService { int checkPackage(int uid, String packageName); List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops); List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); - void getHistoricalOps(int uid, String packageName, in String[] ops, long beginTimeMillis, + void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis, long endTimeMillis, in RemoteCallback callback); - void getHistoricalOpsFromDiskRaw(int uid, String packageName, in String[] ops, + void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops, long beginTimeMillis, long endTimeMillis, in RemoteCallback callback); void offsetHistory(long duration); void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep); 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/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java index 258d081c6ad8..d9fd3b5bd6d8 100644 --- a/core/java/com/android/internal/colorextraction/ColorExtractor.java +++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; -import android.os.Trace; +import android.os.AsyncTask; import android.util.Log; import android.util.SparseArray; @@ -53,11 +53,11 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener protected WallpaperColors mLockColors; public ColorExtractor(Context context) { - this(context, new Tonal(context)); + this(context, new Tonal(context), true /* immediately */); } @VisibleForTesting - public ColorExtractor(Context context, ExtractionType extractionType) { + public ColorExtractor(Context context, ExtractionType extractionType, boolean immediately) { mContext = context; mExtractionType = extractionType; @@ -71,23 +71,48 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener } mOnColorsChangedListeners = new ArrayList<>(); - GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM); - GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK); WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class); if (wallpaperManager == null) { Log.w(TAG, "Can't listen to color changes!"); } else { wallpaperManager.addOnColorsChangedListener(this, null /* handler */); + initExtractColors(wallpaperManager, immediately); + } + } - // Initialize all gradients with the current colors - Trace.beginSection("ColorExtractor#getWallpaperColors"); + private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) { + if (immediately) { mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK); - Trace.endSection(); + extractWallpaperColors(); + } else { + new LoadWallpaperColors().executeOnExecutor( + AsyncTask.THREAD_POOL_EXECUTOR, wallpaperManager); + } + } + + private class LoadWallpaperColors extends AsyncTask<WallpaperManager, Void, Void> { + private WallpaperColors mSystemColors; + private WallpaperColors mLockColors; + @Override + protected Void doInBackground(WallpaperManager... params) { + mSystemColors = params[0].getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + mLockColors = params[0].getWallpaperColors(WallpaperManager.FLAG_LOCK); + return null; + } + @Override + protected void onPostExecute(Void b) { + ColorExtractor.this.mSystemColors = mSystemColors; + ColorExtractor.this.mLockColors = mLockColors; + extractWallpaperColors(); + triggerColorsChanged(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK); } + } - // Initialize all gradients with the current colors + private void extractWallpaperColors() { + GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM); + GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK); extractInto(mSystemColors, systemColors[TYPE_NORMAL], systemColors[TYPE_DARK], 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/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 40d78688cb4c..22884ac9e3fc 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -29,6 +29,7 @@ import android.os.IVold; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; +import android.provider.DeviceConfig; import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -129,21 +130,6 @@ public final class Zygote { public static final int BLASTULA_MANAGEMENT_MESSAGE_BYTES = 8; /** - * If the blastula pool should be created and used to start applications. - * - * Setting this value to false will disable the creation, maintenance, and use of the blastula - * pool. When the blastula pool is disabled the application lifecycle will be identical to - * previous versions of Android. - */ - public static final boolean BLASTULA_POOL_ENABLED = false; - - /** - * File descriptor used for communication between the signal handler and the ZygoteServer poll - * loop. - * */ - protected static FileDescriptor sBlastulaPoolEventFD; - - /** * An extraArg passed when a zygote process is forking a child-zygote, specifying a name * in the abstract socket namespace. This socket name is what the new child zygote * should listen for connections on. @@ -174,43 +160,39 @@ public final class Zygote { private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; /** - * The maximum value that the sBlastulaPoolMax variable may take. This value - * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp. + * The duration to wait before re-checking Zygote related system properties. + * + * Five minutes in milliseconds. */ - static final int BLASTULA_POOL_MAX_LIMIT = 10; + public static final long PROPERTY_CHECK_INTERVAL = 300000; /** - * The minimum value that the sBlastulaPoolMin variable may take. + * @hide for internal use only */ - static final int BLASTULA_POOL_MIN_LIMIT = 1; + public static final int SOCKET_BUFFER_SIZE = 256; + + /** a prototype instance for a future List.toArray() */ + protected static final int[][] INT_ARRAY_2D = new int[0][0]; /** - * The runtime-adjustable maximum Blastula pool size. + * @hide for internal use only. */ - static int sBlastulaPoolMax = BLASTULA_POOL_MAX_LIMIT; + public static final String PRIMARY_SOCKET_NAME = "zygote"; /** - * The runtime-adjustable minimum Blastula pool size. + * @hide for internal use only. */ - static int sBlastulaPoolMin = BLASTULA_POOL_MIN_LIMIT; + public static final String SECONDARY_SOCKET_NAME = "zygote_secondary"; /** - * The runtime-adjustable value used to determine when to re-fill the - * blastula pool. The pool will be re-filled when - * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold. + * @hide for internal use only */ - // TODO (chriswailes): This must be updated at the same time as sBlastulaPoolMax. - static int sBlastulaPoolRefillThreshold = (sBlastulaPoolMax / 2); + public static final String BLASTULA_POOL_PRIMARY_SOCKET_NAME = "blastula_pool"; /** * @hide for internal use only */ - public static final int SOCKET_BUFFER_SIZE = 256; - - private static LocalServerSocket sBlastulaPoolSocket = null; - - /** a prototype instance for a future List.toArray() */ - protected static final int[][] INT_ARRAY_2D = new int[0][0]; + public static final String BLASTULA_POOL_SECONDARY_SOCKET_NAME = "blastula_pool_secondary"; private Zygote() {} @@ -428,70 +410,47 @@ public final class Zygote { protected static native void nativeGetSocketFDs(boolean isPrimary); /** - * Initialize the blastula pool and fill it with the desired number of - * processes. - */ - protected static Runnable initBlastulaPool() { - if (BLASTULA_POOL_ENABLED) { - sBlastulaPoolEventFD = getBlastulaPoolEventFD(); - - return fillBlastulaPool(null); - } else { - return null; - } + * Returns the raw string value of a system property. + * + * Note that Device Config is not available without an application so SystemProperties is used + * instead. + * + * TODO (chriswailes): Cache the system property location in native code and then write a JNI + * function to fetch it. + */ + public static String getSystemProperty(String propertyName, String defaultValue) { + return SystemProperties.get( + String.join(".", + "persist.device_config", + DeviceConfig.RuntimeNative.NAMESPACE, + propertyName), + defaultValue); } /** - * Checks to see if the current policy says that pool should be refilled, and spawns new - * blastulas if necessary. + * Returns the value of a system property converted to a boolean using specific logic. * - * NOTE: This function doesn't need to be guarded with BLASTULA_POOL_ENABLED because it is - * only called from contexts that are only valid if the pool is enabled. + * Note that Device Config is not available without an application so SystemProperties is used + * instead. * - * @param sessionSocketRawFDs Anonymous session sockets that are currently open - * @return In the Zygote process this function will always return null; in blastula processes - * this function will return a Runnable object representing the new application that is - * passed up from blastulaMain. - */ - protected static Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); - - int blastulaPoolCount = getBlastulaPoolCount(); - - int numBlastulasToSpawn = sBlastulaPoolMax - blastulaPoolCount; - - if (blastulaPoolCount < sBlastulaPoolMin - || numBlastulasToSpawn >= sBlastulaPoolRefillThreshold) { - - // Disable some VM functionality and reset some system values - // before forking. - ZygoteHooks.preFork(); - resetNicePriority(); - - while (blastulaPoolCount++ < sBlastulaPoolMax) { - Runnable caller = forkBlastula(sessionSocketRawFDs); - - if (caller != null) { - return caller; - } - } - - // Re-enable runtime services for the Zygote. Blastula services - // are re-enabled in specializeBlastula. - ZygoteHooks.postForkCommon(); - - Log.i("zygote", "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); - } - - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - - return null; + * @see SystemProperties.getBoolean + * + * TODO (chriswailes): Cache the system property location in native code and then write a JNI + * function to fetch it. + */ + public static boolean getSystemPropertyBoolean(String propertyName, Boolean defaultValue) { + return SystemProperties.getBoolean( + String.join(".", + "persist.device_config", + DeviceConfig.RuntimeNative.NAMESPACE, + propertyName), + defaultValue); } /** * @return Number of blastulas currently in the pool */ - private static int getBlastulaPoolCount() { + static int getBlastulaPoolCount() { return nativeGetBlastulaPoolCount(); } @@ -501,7 +460,7 @@ public final class Zygote { * @return The event FD used for communication between the signal handler and the ZygoteServer * poll loop */ - private static FileDescriptor getBlastulaPoolEventFD() { + static FileDescriptor getBlastulaPoolEventFD() { FileDescriptor fd = new FileDescriptor(); fd.setInt$(nativeGetBlastulaPoolEventFD()); @@ -518,7 +477,8 @@ public final class Zygote { * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. */ - private static Runnable forkBlastula(int[] sessionSocketRawFDs) { + static Runnable forkBlastula(LocalServerSocket blastulaPoolSocket, + int[] sessionSocketRawFDs) { FileDescriptor[] pipeFDs = null; try { @@ -532,7 +492,7 @@ public final class Zygote { if (pid == 0) { IoUtils.closeQuietly(pipeFDs[0]); - return blastulaMain(pipeFDs[1]); + return blastulaMain(blastulaPoolSocket, pipeFDs[1]); } else { // The read-end of the pipe will be closed by the native code. // See removeBlastulaTableEntry(); @@ -553,7 +513,8 @@ public final class Zygote { * of the ZygoteServer. * @return A runnable oject representing the new application. */ - static Runnable blastulaMain(FileDescriptor writePipe) { + private static Runnable blastulaMain(LocalServerSocket blastulaPoolSocket, + FileDescriptor writePipe) { final int pid = Process.myPid(); LocalSocket sessionSocket = null; @@ -563,7 +524,7 @@ public final class Zygote { while (true) { try { - sessionSocket = sBlastulaPoolSocket.accept(); + sessionSocket = blastulaPoolSocket.accept(); BufferedReader blastulaReader = new BufferedReader(new InputStreamReader(sessionSocket.getInputStream())); @@ -611,7 +572,7 @@ public final class Zygote { System.exit(-1); } finally { IoUtils.closeQuietly(sessionSocket); - IoUtils.closeQuietly(sBlastulaPoolSocket); + IoUtils.closeQuietly(blastulaPoolSocket); } try { @@ -660,7 +621,7 @@ public final class Zygote { * exception if an invalid arugment is encountered. * @param args The arguments to test */ - static void validateBlastulaCommand(ZygoteArguments args) { + private static void validateBlastulaCommand(ZygoteArguments args) { if (args.mAbiListQuery) { throw new IllegalArgumentException(BLASTULA_ERROR_PREFIX + "--query-abi-list"); } else if (args.mPidQuery) { @@ -851,20 +812,6 @@ public final class Zygote { } /** - * Creates a managed object representing the Blastula pool socket that has - * already been initialized and bound by init. - * - * TODO (chriswailes): Move the name selection logic into this function. - * - * @throws RuntimeException when open fails - */ - static void createBlastulaSocket(String socketName) { - if (BLASTULA_POOL_ENABLED && sBlastulaPoolSocket == null) { - sBlastulaPoolSocket = createManagedSocketFromInitSocket(socketName); - } - } - - /** * Creates a managed LocalServerSocket object using a file descriptor * created by an init.rc script. The init scripts that specify the * sockets name can be found in system/core/rootdir. The socket is bound diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 9cf7e2770e86..8a878e22a7e3 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -338,13 +338,25 @@ class ZygoteConnection { private final MetricsLogger mMetricsLogger = new MetricsLogger(); private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger(); + private int mHiddenApiAccessLogSampleRate = 0; + + public static void setHiddenApiAccessLogSampleRate(int sampleRate) { + sInstance.mHiddenApiAccessLogSampleRate = sampleRate; + } public static HiddenApiUsageLogger getInstance() { return HiddenApiUsageLogger.sInstance; } - public void hiddenApiUsed(String packageName, String signature, + public void hiddenApiUsed(int sampledValue, String packageName, String signature, int accessMethod, boolean accessDenied) { + if (sampledValue < mHiddenApiAccessLogSampleRate) { + logUsage(packageName, signature, accessMethod, accessDenied); + } + } + + private void logUsage(String packageName, String signature, int accessMethod, + boolean accessDenied) { int accessMethodMetric = HiddenApiUsageLogger.ACCESS_METHOD_NONE; switch(accessMethod) { case HiddenApiUsageLogger.ACCESS_METHOD_NONE: @@ -375,6 +387,7 @@ class ZygoteConnection { private void handleHiddenApiAccessLogSampleRate(int samplingRate) { try { ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate); + HiddenApiUsageLogger.setHiddenApiAccessLogSampleRate(samplingRate); ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance()); mSocketOutStream.writeInt(0); } catch (IOException ioe) { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index e132abd7e4cb..7cddf7588b28 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -755,7 +755,7 @@ public class ZygoteInit { } public static void main(String argv[]) { - ZygoteServer zygoteServer = new ZygoteServer(); + ZygoteServer zygoteServer = null; // Mark zygote start. This ensures that thread creation will throw // an error. @@ -783,7 +783,7 @@ public class ZygoteInit { RuntimeInit.enableDdms(); boolean startSystemServer = false; - String socketName = "zygote"; + String zygoteSocketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; i++) { @@ -794,26 +794,19 @@ public class ZygoteInit { } else if (argv[i].startsWith(ABI_LIST_ARG)) { abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { - socketName = argv[i].substring(SOCKET_NAME_ARG.length()); + zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } + final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME); + if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } - // TODO (chriswailes): Wrap these three calls in a helper function? - final String blastulaSocketName = - socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME) - ? ZygoteProcess.BLASTULA_POOL_SOCKET_NAME - : ZygoteProcess.BLASTULA_POOL_SECONDARY_SOCKET_NAME; - - zygoteServer.createZygoteSocket(socketName); - Zygote.createBlastulaSocket(blastulaSocketName); - - Zygote.getSocketFDs(socketName.equals(ZygoteProcess.ZYGOTE_SOCKET_NAME)); + Zygote.getSocketFDs(isPrimaryZygote); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. @@ -846,8 +839,10 @@ public class ZygoteInit { ZygoteHooks.stopZygoteNoThreadCreation(); + zygoteServer = new ZygoteServer(isPrimaryZygote); + if (startSystemServer) { - Runnable r = forkSystemServer(abiList, socketName, zygoteServer); + Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. @@ -857,23 +852,18 @@ public class ZygoteInit { } } - // If the return value is null then this is the zygote process - // returning to the normal control flow. If it returns a Runnable - // object then this is a blastula that has finished specializing. - caller = Zygote.initBlastulaPool(); + Log.i(TAG, "Accepting command socket connections"); - if (caller == null) { - Log.i(TAG, "Accepting command socket connections"); - - // The select loop returns early in the child process after a fork and - // loops forever in the zygote. - caller = zygoteServer.runSelectLoop(abiList); - } + // The select loop returns early in the child process after a fork and + // loops forever in the zygote. + caller = zygoteServer.runSelectLoop(abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); throw ex; } finally { - zygoteServer.closeServerSocket(); + if (zygoteServer != null) { + zygoteServer.closeServerSocket(); + } } // We're in the child process and have exited the select loop. Proceed to execute the @@ -894,8 +884,8 @@ public class ZygoteInit { } private static void waitForSecondaryZygote(String socketName) { - String otherZygoteName = ZygoteProcess.ZYGOTE_SOCKET_NAME.equals(socketName) - ? ZygoteProcess.ZYGOTE_SECONDARY_SOCKET_NAME : ZygoteProcess.ZYGOTE_SOCKET_NAME; + String otherZygoteName = Zygote.PRIMARY_SOCKET_NAME.equals(socketName) + ? Zygote.SECONDARY_SOCKET_NAME : Zygote.PRIMARY_SOCKET_NAME; ZygoteProcess.waitForConnectionToZygote(otherZygoteName); } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index a78c095a8deb..2c17540eb6c6 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -20,12 +20,17 @@ import static android.system.OsConstants.POLLIN; import android.net.LocalServerSocket; import android.net.LocalSocket; +import android.os.SystemClock; +import android.os.Trace; +import android.provider.DeviceConfig; import android.system.ErrnoException; import android.system.Os; import android.system.StructPollfd; import android.util.Log; import android.util.Slog; +import dalvik.system.ZygoteHooks; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.FileDescriptor; @@ -38,7 +43,7 @@ import java.util.ArrayList; * Provides functions to wait for commands on a UNIX domain socket, and fork * off child processes that inherit the initial state of the VM.% * - * Please see {@link ZygoteConnection.Arguments} for documentation on the + * Please see {@link ZygoteArguments} for documentation on the * client protocol. */ class ZygoteServer { @@ -46,11 +51,48 @@ class ZygoteServer { public static final String TAG = "ZygoteServer"; /** + * The maximim value that will be accepted from the BLASTULA_POOL_SIZE_MAX device property. + * is a mirror of BLASTULA_POOL_MAX_LIMIT found in com_android_internal_os_Zygote.cpp. + */ + private static final int BLASTULA_POOL_SIZE_MAX_LIMIT = 100; + + /** + * The minimum value that will be accepted from the BLASTULA_POOL_SIZE_MIN device property. + */ + private static final int BLASTULA_POOL_SIZE_MIN_LIMIT = 1; + + /** The default value used for the BLASTULA_POOL_SIZE_MAX device property */ + private static final String BLASTULA_POOL_SIZE_MAX_DEFAULT = "10"; + + /** The default value used for the BLASTULA_POOL_SIZE_MIN device property */ + private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1"; + + /** + * If the blastula pool should be created and used to start applications. + * + * Setting this value to false will disable the creation, maintenance, and use of the blastula + * pool. When the blastula pool is disabled the application lifecycle will be identical to + * previous versions of Android. + */ + private boolean mBlastulaPoolEnabled = false; + + /** * Listening socket that accepts new server connections. */ private LocalServerSocket mZygoteSocket; /** + * The name of the blastula socket to use if the blastula pool is enabled. + */ + private LocalServerSocket mBlastulaPoolSocket; + + /** + * File descriptor used for communication between the signal handler and the ZygoteServer poll + * loop. + * */ + private FileDescriptor mBlastulaPoolEventFD; + + /** * Whether or not mZygoteSocket's underlying FD should be closed directly. * If mZygoteSocket is created with an existing FD, closing the socket does * not close the FD and it must be closed explicitly. If the socket is created @@ -64,25 +106,55 @@ class ZygoteServer { */ private boolean mIsForkChild; - ZygoteServer() { } + /** + * The runtime-adjustable maximum Blastula pool size. + */ + private int mBlastulaPoolSizeMax = 0; - void setForkChild() { - mIsForkChild = true; + /** + * The runtime-adjustable minimum Blastula pool size. + */ + private int mBlastulaPoolSizeMin = 0; + + /** + * The runtime-adjustable value used to determine when to re-fill the + * blastula pool. The pool will be re-filled when + * (sBlastulaPoolMax - gBlastulaPoolCount) >= sBlastulaPoolRefillThreshold. + */ + private int mBlastulaPoolRefillThreshold = 0; + + ZygoteServer() { + mBlastulaPoolEventFD = null; + mZygoteSocket = null; + mBlastulaPoolSocket = null; } /** - * Creates a managed object representing the Zygote socket that has already - * been initialized and bound by init. + * Initialize the Zygote server with the Zygote server socket, blastula pool server socket, + * and blastula pool event FD. * - * TODO (chriswailes): Move the name selection logic into this function. - * - * @throws RuntimeException when open fails + * @param isPrimaryZygote If this is the primary Zygote or not. */ - void createZygoteSocket(String socketName) { - if (mZygoteSocket == null) { - mZygoteSocket = Zygote.createManagedSocketFromInitSocket(socketName); - mCloseSocketFd = true; + ZygoteServer(boolean isPrimaryZygote) { + mBlastulaPoolEventFD = Zygote.getBlastulaPoolEventFD(); + + if (isPrimaryZygote) { + mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME); + mBlastulaPoolSocket = + Zygote.createManagedSocketFromInitSocket( + Zygote.BLASTULA_POOL_PRIMARY_SOCKET_NAME); + } else { + mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME); + mBlastulaPoolSocket = + Zygote.createManagedSocketFromInitSocket( + Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME); } + + fetchBlastulaPoolPolicyProps(); + } + + void setForkChild() { + mIsForkChild = true; } /** @@ -151,6 +223,102 @@ class ZygoteServer { return mZygoteSocket.getFileDescriptor(); } + private void fetchBlastulaPoolPolicyProps() { + final String blastulaPoolSizeMaxPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX, + BLASTULA_POOL_SIZE_MAX_DEFAULT); + + if (!blastulaPoolSizeMaxPropString.isEmpty()) { + mBlastulaPoolSizeMax = + Integer.min( + Integer.parseInt(blastulaPoolSizeMaxPropString), + BLASTULA_POOL_SIZE_MAX_LIMIT); + } + + final String blastulaPoolSizeMinPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN, + BLASTULA_POOL_SIZE_MIN_DEFAULT); + + if (!blastulaPoolSizeMinPropString.isEmpty()) { + mBlastulaPoolSizeMin = + Integer.max( + Integer.parseInt(blastulaPoolSizeMinPropString), + BLASTULA_POOL_SIZE_MIN_LIMIT); + } + + final String blastulaPoolRefillThresholdPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD, + Integer.toString(mBlastulaPoolSizeMax / 2)); + + if (!blastulaPoolRefillThresholdPropString.isEmpty()) { + mBlastulaPoolRefillThreshold = + Integer.min( + Integer.parseInt(blastulaPoolRefillThresholdPropString), + mBlastulaPoolSizeMax); + } + + } + + private long mLastPropCheckTimestamp = 0; + + private void fetchBlastulaPoolPolicyPropsWithMinInterval() { + final long currentTimestamp = SystemClock.elapsedRealtime(); + + if (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL) { + fetchBlastulaPoolPolicyProps(); + mLastPropCheckTimestamp = currentTimestamp; + } + } + + /** + * Checks to see if the current policy says that pool should be refilled, and spawns new + * blastulas if necessary. + * + * @param sessionSocketRawFDs Anonymous session sockets that are currently open + * @return In the Zygote process this function will always return null; in blastula processes + * this function will return a Runnable object representing the new application that is + * passed up from blastulaMain. + */ + private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { + if (mBlastulaPoolEnabled) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); + + int blastulaPoolCount = Zygote.getBlastulaPoolCount(); + int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount; + + if (blastulaPoolCount < mBlastulaPoolSizeMin + || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) { + + // Disable some VM functionality and reset some system values + // before forking. + ZygoteHooks.preFork(); + Zygote.resetNicePriority(); + + while (blastulaPoolCount++ < mBlastulaPoolSizeMax) { + Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs); + + if (caller != null) { + return caller; + } + } + + // Re-enable runtime services for the Zygote. Blastula services + // are re-enabled in specializeBlastula. + ZygoteHooks.postForkCommon(); + + Log.i("zygote", + "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); + } + + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + return null; + } + /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's @@ -164,6 +332,8 @@ class ZygoteServer { peers.add(null); while (true) { + fetchBlastulaPoolPolicyPropsWithMinInterval(); + int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs(); // Space for all of the socket FDs, the Blastula Pool Event FD, and @@ -181,7 +351,7 @@ class ZygoteServer { final int blastulaPoolEventFDIndex = pollIndex; pollFDs[pollIndex] = new StructPollfd(); - pollFDs[pollIndex].fd = Zygote.sBlastulaPoolEventFD; + pollFDs[pollIndex].fd = mBlastulaPoolEventFD; pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; @@ -316,7 +486,7 @@ class ZygoteServer { .mapToInt(fd -> fd.getInt$()) .toArray(); - final Runnable command = Zygote.fillBlastulaPool(sessionSocketRawFDs); + final Runnable command = fillBlastulaPool(sessionSocketRawFDs); if (command != null) { return command; diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java index a8ad8102c610..c46f86792764 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -137,6 +137,7 @@ public class DividerSnapAlgorithm { mDismissStartTarget = mTargets.get(0); mDismissEndTarget = mTargets.get(mTargets.size() - 1); mMiddleTarget = mTargets.get(mTargets.size() / 2); + mMiddleTarget.isMiddleTarget = true; } /** @@ -438,6 +439,8 @@ public class DividerSnapAlgorithm { public final int flag; + public boolean isMiddleTarget; + /** * Multiplier used to calculate distance to snap position. The lower this value, the harder * it's to snap on this target diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index 21558d3d3518..e90a8d5826f2 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -329,13 +329,13 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, } /** - * Maximize the window by moving it to the maximized workspace stack. + * Maximize or restore the window by moving it to the maximized or freeform workspace stack. **/ - private void maximizeWindow() { + private void toggleFreeformWindowingMode() { Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback(); if (callback != null) { try { - callback.exitFreeformMode(); + callback.toggleFreeformWindowingMode(); } catch (RemoteException ex) { Log.e(TAG, "Cannot change task workspace."); } @@ -395,7 +395,7 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, @Override public boolean onSingleTapUp(MotionEvent e) { if (mClickTarget == mMaximize) { - maximizeWindow(); + toggleFreeformWindowingMode(); } else if (mClickTarget == mClose) { mOwner.dispatchOnWindowDismissed( true /*finishTask*/, false /*suppressWindowTransition*/); 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..30e9937c7d7c 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -163,6 +163,7 @@ cc_library_shared { "android_media_AudioSystem.cpp", "android_media_AudioTrack.cpp", "android_media_AudioAttributes.cpp", + "android_media_AudioProductStrategies.cpp", "android_media_DeviceCallback.cpp", "android_media_JetPlayer.cpp", "android_media_MediaMetricsJNI.cpp", @@ -230,6 +231,7 @@ cc_library_shared { ], static_libs: [ + "libasync_safe", "libgif", "libseccomp_policy", "libgrallocusage", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d69d416e8401..019ade9a3914 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -109,6 +109,7 @@ extern int register_android_media_AudioRecord(JNIEnv *env); extern int register_android_media_AudioSystem(JNIEnv *env); extern int register_android_media_AudioTrack(JNIEnv *env); extern int register_android_media_AudioAttributes(JNIEnv *env); +extern int register_android_media_AudioProductStrategies(JNIEnv *env); extern int register_android_media_MicrophoneInfo(JNIEnv *env); extern int register_android_media_JetPlayer(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); @@ -232,8 +233,15 @@ extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; -// Feature flag name for Garbage Collector type. -static const char* GCTYPE = "gctype"; +// Feature flag name to enable/disable generational garbage collection in ART's +// Concurrent Copying (CC) garbage collector. +static const char* ENABLE_GENERATIONAL_CC = "enable_generational_cc"; +// Runtime option enabling generational garbage collection in ART's Concurrent +// Copying (CC) garbage collector. +static const char* kGenerationalCCRuntimeOption = "-Xgc:generational_cc"; +// Runtime option disabling generational garbage collection in ART's Concurrent +// Copying (CC) garbage collector. +static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc"; static AndroidRuntime* gCurRuntime = NULL; @@ -667,6 +675,11 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX]; std::string fingerprintBuf; char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX]; + char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX]; + + if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) { + ALOGI("Boot image: '%s'\n", bootImageBuf); + } bool checkJni = false; property_get("dalvik.vm.checkjni", propBuf, ""); @@ -780,17 +793,21 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) addOption("-XX:LowMemoryMode"); } - std::string gc_type_override = - server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, - GCTYPE, - /*default_value=*/ ""); - std::string gc_type_override_temp; - if (gc_type_override.empty()) { - parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:"); - } else { - // Copy the string so it doesn't go out of scope since addOption does not make a copy. - gc_type_override_temp = "-Xgc:" + gc_type_override; - addOption(gc_type_override_temp.c_str()); + /* + * Garbage-collection related options. + */ + parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:"); + + // If it set, honor the "enable_generational_cc" device configuration; + // otherwise, let the runtime use its default behavior. + std::string enable_generational_cc = + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_GENERATIONAL_CC, + /*default_value=*/ ""); + if (enable_generational_cc == "true") { + addOption(kGenerationalCCRuntimeOption); + } else if (enable_generational_cc == "false") { + addOption(kNoGenerationalCCRuntimeOption); } parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC="); @@ -1490,6 +1507,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_AudioRecord), REG_JNI(register_android_media_AudioTrack), REG_JNI(register_android_media_AudioAttributes), + REG_JNI(register_android_media_AudioProductStrategies), REG_JNI(register_android_media_JetPlayer), REG_JNI(register_android_media_MicrophoneInfo), REG_JNI(register_android_media_RemoteDisplay), 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_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp new file mode 100644 index 000000000000..a18e80a4e6e1 --- /dev/null +++ b/core/jni/android_media_AudioProductStrategies.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioProductStrategies-JNI" + +#include <inttypes.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include "core_jni_helpers.h" + +#include <utils/Log.h> +#include <vector> + +#include <media/AudioSystem.h> +#include <media/AudioPolicy.h> + +#include <nativehelper/ScopedUtfChars.h> + +#include "android_media_AudioAttributes.h" +#include "android_media_AudioErrors.h" + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +static const char* const kClassPathName = "android/media/audiopolicy/AudioProductStrategies"; +static const char* const kAudioProductStrategyClassPathName = + "android/media/audiopolicy/AudioProductStrategy"; + +static const char* const kAudioAttributesGroupsClassPathName = + "android/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup"; + +static jclass gAudioProductStrategyClass; +static jmethodID gAudioProductStrategyCstor; +static struct { + jfieldID mAudioAttributesGroups; + jfieldID mName; + jfieldID mId; +} gAudioProductStrategyFields; + +static jclass gAudioAttributesGroupClass; +static jmethodID gAudioAttributesGroupCstor; +static struct { + jfieldID mGroupId; + jfieldID mLegacyStreamType; + jfieldID mAudioAttributes; +} gAudioAttributesGroupsFields; + +static jclass gArrayListClass; +static struct { + jmethodID add; + jmethodID toArray; +} gArrayListMethods; + + +static jint convertAudioProductStrategiesFromNative( + JNIEnv *env, jobject *jAudioStrategy, const AudioProductStrategy &strategy) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jobjectArray jAudioAttributesGroups = NULL; + jobjectArray jAudioAttributes = NULL; + jobject jAudioAttribute = NULL; + jstring jName = NULL; + jint jStrategyId = NULL; + jint numAttributesGroups; + size_t indexGroup = 0; + + jName = env->NewStringUTF(strategy.getName().c_str()); + jStrategyId = static_cast<jint>(strategy.getId()); + + // Audio Attributes Group array + std::map<int, std::vector<AudioAttributes> > groups; + for (const auto &attr : strategy.getAudioAttributes()) { + int attrGroupId = attr.getGroupId(); + groups[attrGroupId].push_back(attr); + } + numAttributesGroups = groups.size(); + + jAudioAttributesGroups = env->NewObjectArray(numAttributesGroups, gAudioAttributesGroupClass, NULL); + + for (const auto &iter : groups) { + std::vector<AudioAttributes> audioAttributesGroups = iter.second; + jint numAttributes = audioAttributesGroups.size(); + jint jGroupId = iter.first; + jint jLegacyStreamType = audioAttributesGroups.front().getStreamType(); + + jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + goto exit; + } + for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) { + auto attributes = audioAttributesGroups[j].getAttributes(); + + jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute); + } + jobject jAudioAttributesGroup = env->NewObject(gAudioAttributesGroupClass, + gAudioAttributesGroupCstor, + jGroupId, + jLegacyStreamType, + jAudioAttributes); + env->SetObjectArrayElement(jAudioAttributesGroups, indexGroup++, jAudioAttributesGroup); + + if (jAudioAttributes != NULL) { + env->DeleteLocalRef(jAudioAttributes); + jAudioAttributes = NULL; + } + if (jAudioAttribute != NULL) { + env->DeleteLocalRef(jAudioAttribute); + jAudioAttribute = NULL; + } + if (jAudioAttributesGroup != NULL) { + env->DeleteLocalRef(jAudioAttributesGroup); + jAudioAttributesGroup = NULL; + } + } + *jAudioStrategy = env->NewObject(gAudioProductStrategyClass, gAudioProductStrategyCstor, + jName, + jStrategyId, + jAudioAttributesGroups); +exit: + if (jAudioAttributes != NULL) { + env->DeleteLocalRef(jAudioAttributes); + } + if (jAudioAttribute != NULL) { + env->DeleteLocalRef(jAudioAttribute); + jAudioAttribute = NULL; + } + if (jAudioAttributesGroups != NULL) { + env->DeleteLocalRef(jAudioAttributesGroups); + } + if (jName != NULL) { + env->DeleteLocalRef(jName); + } + return jStatus; +} + +static jint +android_media_AudioSystem_listAudioProductStrategies(JNIEnv *env, jobject clazz, + jobject jStrategies) +{ + if (env == NULL) { + return AUDIO_JAVA_DEAD_OBJECT; + } + if (jStrategies == NULL) { + ALOGE("listAudioProductStrategies NULL AudioProductStrategies"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jStrategies, gArrayListClass)) { + ALOGE("listAudioProductStrategies not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + AudioProductStrategyVector strategies; + jint jStatus; + jobject jStrategy = NULL; + + status = AudioSystem::listAudioProductStrategies(strategies); + if (status != NO_ERROR) { + ALOGE("AudioSystem::listAudioProductStrategies error %d", status); + return nativeToJavaStatus(status); + } + for (const auto &strategy : strategies) { + jStatus = convertAudioProductStrategiesFromNative(env, &jStrategy, strategy); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->CallBooleanMethod(jStrategies, gArrayListMethods.add, jStrategy); + } +exit: + if (jStrategy != NULL) { + env->DeleteLocalRef(jStrategy); + } + return jStatus; +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + {"native_list_audio_product_strategies", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioSystem_listAudioProductStrategies}, +}; + +int register_android_media_AudioProductStrategies(JNIEnv *env) +{ + jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); + gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); + gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); + gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, + "toArray", "()[Ljava/lang/Object;"); + + jclass audioProductStrategyClass = FindClassOrDie(env, kAudioProductStrategyClassPathName); + gAudioProductStrategyClass = MakeGlobalRefOrDie(env, audioProductStrategyClass); + gAudioProductStrategyCstor = GetMethodIDOrDie( + env, audioProductStrategyClass, "<init>", + "(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V"); + gAudioProductStrategyFields.mAudioAttributesGroups = GetFieldIDOrDie( + env, audioProductStrategyClass, "mAudioAttributesGroups", + "[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;"); + gAudioProductStrategyFields.mName = GetFieldIDOrDie( + env, audioProductStrategyClass, "mName", "Ljava/lang/String;"); + gAudioProductStrategyFields.mId = GetFieldIDOrDie( + env, audioProductStrategyClass, "mId", "I"); + + jclass audioAttributesGroupClass = FindClassOrDie(env, kAudioAttributesGroupsClassPathName); + gAudioAttributesGroupClass = MakeGlobalRefOrDie(env, audioAttributesGroupClass); + gAudioAttributesGroupCstor = GetMethodIDOrDie(env, audioAttributesGroupClass, "<init>", + "(II[Landroid/media/AudioAttributes;)V"); + gAudioAttributesGroupsFields.mGroupId = GetFieldIDOrDie( + env, audioAttributesGroupClass, "mGroupId", "I"); + gAudioAttributesGroupsFields.mLegacyStreamType = GetFieldIDOrDie( + env, audioAttributesGroupClass, "mLegacyStreamType", "I"); + gAudioAttributesGroupsFields.mAudioAttributes = GetFieldIDOrDie( + env, audioAttributesGroupClass, "mAudioAttributes", + "[Landroid/media/AudioAttributes;"); + + env->DeleteLocalRef(audioProductStrategyClass); + env->DeleteLocalRef(audioAttributesGroupClass); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} 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_Debug.cpp b/core/jni/android_os_Debug.cpp index e9035ed102ae..195fe58f0beb 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -737,6 +737,25 @@ static jstring android_os_Debug_getUnreachableMemory(JNIEnv* env, jobject clazz, return env->NewStringUTF(s.c_str()); } +static jlong android_os_Debug_getFreeZramKb(JNIEnv* env, jobject clazz) { + + jlong zramFreeKb = 0; + + std::string status_path = android::base::StringPrintf("/proc/meminfo"); + UniqueFile file = MakeUniqueFile(status_path.c_str(), "re"); + + char line[256]; + while (file != nullptr && fgets(line, sizeof(line), file.get())) { + jlong v; + if (sscanf(line, "SwapFree: %" SCNd64 " kB", &v) == 1) { + zramFreeKb = v; + break; + } + } + + return zramFreeKb; +} + /* * JNI registration. */ @@ -778,6 +797,8 @@ static const JNINativeMethod gMethods[] = { (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout }, { "getUnreachableMemory", "(IZ)Ljava/lang/String;", (void*)android_os_Debug_getUnreachableMemory }, + { "getZramFreeKb", "()J", + (void*)android_os_Debug_getFreeZramKb }, }; int register_android_os_Debug(JNIEnv *env) 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..ecc2dd0d3598 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); }); } } @@ -1083,6 +1029,10 @@ static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject claz proxy->setForceDark(enable); } +static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { + RenderProxy::preload(); +} + // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- @@ -1198,6 +1148,7 @@ static const JNINativeMethod gMethods[] = { { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority }, { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers }, { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark }, + { "preload", "()V", (void*)android_view_ThreadedRenderer_preload }, }; static JavaVM* mJvm = nullptr; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5cecf66a593c..15ceca96313e 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,99 @@ 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 && errno != EEXIST) { + 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 + && errno != EEXIST) { + 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)); + } + + 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) { -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"); + 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 && errno != EEXIST) { + fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", + sandboxSource.c_str(), strerror(errno))); } } @@ -680,21 +751,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 +846,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 +864,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"); } @@ -1288,6 +1358,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, SetSchedulerPolicy(fail_fn); + __android_log_close(); + stats_log_close(); + const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr; const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr; @@ -1384,8 +1457,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/jni/runtime_native_boot-flags-test.sh b/core/jni/runtime_native_boot-flags-test.sh index 66e18bb19c44..01f37f07e5ca 100755 --- a/core/jni/runtime_native_boot-flags-test.sh +++ b/core/jni/runtime_native_boot-flags-test.sh @@ -172,12 +172,14 @@ function check_no_zygote_gc_runtime_option { done } -# test_android_runtime_flag FLAG VALUE -# ------------------------------------ -# Test device configuration FLAG with VALUE. +# test_android_runtime_flag FLAG VALUE GC_RUNTIME_OPTION +# ------------------------------------------------------ +# Test device configuration FLAG with VALUE. Check that GC_RUNTIME_OPTION is +# passed as GC Runtime option by the zygote. function test_android_runtime_flag { local flag=$1 local value=$2 + local gc_runtime_option=$3 # Persistent system property (set after a reboot) associated with the device # configuration flag. @@ -196,21 +198,21 @@ function test_android_runtime_flag { local context="Flag set, before reboot" check_device_config_flag "$context" "$flag" "$value" check_system_property "$context" "$prop" "$value" - check_no_zygote_gc_runtime_option "$context" "$value" + check_no_zygote_gc_runtime_option "$context" "$gc_runtime_option" # Reboot device for the flag value to take effect. reboot_and_wait_for_device context="Flag set, after 1st reboot" check_device_config_flag "$context" "$flag" "$value" check_system_property "$context" "$prop" "$value" - check_zygote_gc_runtime_option "$context" "$value" + check_zygote_gc_runtime_option "$context" "$gc_runtime_option" # Reboot device a second time and check that the state has persisted. reboot_and_wait_for_device context="Flag set, after 2nd reboot" check_device_config_flag "$context" "$flag" "$value" check_system_property "$context" "$prop" "$value" - check_zygote_gc_runtime_option "$context" "$value" + check_zygote_gc_runtime_option "$context" "$gc_runtime_option" say "Unsetting device configuration flag..." adb shell device_config delete "$namespace" "$flag" >/dev/null @@ -222,7 +224,7 @@ function test_android_runtime_flag { context="Flag unset, after 3rd reboot" check_no_device_config_flag "$context" "$flag" check_no_system_property "$context" "$prop" - check_no_zygote_gc_runtime_option "$context" "$value" + check_no_zygote_gc_runtime_option "$context" "$gc_runtime_option" } # Enumerate Zygote processes. @@ -232,9 +234,9 @@ case $(adb shell getprop ro.zygote) in (zygote32_64|zygote64_32) zygotes="zygote zygote64";; esac -# Test "gctype" flag values. -test_android_runtime_flag gctype nogenerational_cc -test_android_runtime_flag gctype generational_cc +# Test "enable_generational_cc" flag values. +test_android_runtime_flag enable_generational_cc false nogenerational_cc +test_android_runtime_flag enable_generational_cc true generational_cc if [[ "$exit_status" -eq 0 ]]; then banner "All tests passed." 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/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index c9957f369473..cccb40d51722 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -453,6 +453,8 @@ message GlobalSettingsProto { optional SettingProto game_driver_blacklists = 14; // ANGLE - Show a dialog box when ANGLE is selected for the currently running PKG optional SettingProto show_angle_in_use_dialog = 15; + // Game Driver - List of libraries in sphal accessible by Game Driver + optional SettingProto game_driver_sphal_libraries = 16; } optional Gpu gpu = 59; 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/layout/notification_material_media_seekbar.xml b/core/res/res/layout/notification_material_media_seekbar.xml index 1b691d68a201..c23ca835433e 100644 --- a/core/res/res/layout/notification_material_media_seekbar.xml +++ b/core/res/res/layout/notification_material_media_seekbar.xml @@ -30,6 +30,7 @@ android:maxHeight="3dp" android:paddingTop="24dp" android:paddingBottom="24dp" + android:clickable="true" android:layout_marginBottom="-24dp" android:layout_marginTop="-12dp" android:splitTrack="false" 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..09fb663468f5 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> @@ -4291,6 +4291,13 @@ <!-- Title text to append when the display is secure. [CHAR LIMIT=30] --> <string name="display_manager_overlay_display_secure_suffix">, secure</string> + <!-- Activity starter --> + <!-- Toast message for blocking background activity starts feature running in permissive mode --> + <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See go/q-bg-block.</string> + + <!-- Toast message for blocking background activity starts feature running in enforcing mode --> + <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See go/q-bg-block. </string> + <!-- Keyguard strings --> <!-- Message shown in pattern unlock after some number of unsuccessful attempts --> <string name="kg_forgot_pattern_button_text">Forgot Pattern</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a1bafbf8de69..1ebd6e931ab6 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" /> @@ -762,6 +763,8 @@ <java-symbol type="string" name="display_manager_hdmi_display_name" /> <java-symbol type="string" name="display_manager_overlay_display_name" /> <java-symbol type="string" name="display_manager_overlay_display_secure_suffix" /> + <java-symbol type="string" name="activity_starter_block_bg_activity_starts_permissive" /> + <java-symbol type="string" name="activity_starter_block_bg_activity_starts_enforcing" /> <java-symbol type="string" name="display_manager_overlay_display_title" /> <java-symbol type="string" name="double_tap_toast" /> <java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" /> @@ -2755,6 +2758,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 +3588,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 +3658,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/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index a33de7bb2d9d..a71000bd220c 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -182,8 +182,7 @@ public class UriTest extends TestCase { uri = Uri.parse("http://bob%40lee%3ajr@local%68ost:4%32"); assertEquals("bob@lee:jr", uri.getUserInfo()); - assertEquals("localhost", uri.getHost()); - assertEquals(42, uri.getPort()); + assertEquals("localhost:42", uri.getHost()); uri = Uri.parse("http://localhost"); assertEquals("localhost", uri.getHost()); diff --git a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java index 02a9adf4fb4d..bff6b5fb3d24 100644 --- a/core/tests/coretests/src/android/provider/DocumentsProviderTest.java +++ b/core/tests/coretests/src/android/provider/DocumentsProviderTest.java @@ -60,7 +60,8 @@ public class DocumentsProviderTest extends ProviderTestCase2<TestDocumentsProvid DocumentsContract.buildDocumentUri(TestDocumentsProvider.AUTHORITY, DOCUMENT_ID); try (ContentProviderClient client = mResolver.acquireUnstableContentProviderClient(docUri)) { - final Path actual = DocumentsContract.findDocumentPath(client, docUri); + final Path actual = DocumentsContract.findDocumentPath( + ContentResolver.wrap(client), docUri); assertEquals(expected, actual); } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 4d2f005998c3..46cac7a49f13 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -122,6 +122,7 @@ public class SettingsBackupTest { Settings.Global.APP_OPS_CONSTANTS, Settings.Global.APP_STANDBY_ENABLED, Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, + Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, Settings.Global.ASSISTED_GPS_ENABLED, Settings.Global.AUDIO_SAFE_VOLUME_STATE, Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES, @@ -494,6 +495,7 @@ public class SettingsBackupTest { Settings.Global.GAME_DRIVER_BLACKLISTS, Settings.Global.GAME_DRIVER_BLACKLIST, Settings.Global.GAME_DRIVER_WHITELIST, + Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, @@ -621,6 +623,7 @@ public class SettingsBackupTest { Settings.Secure.DISABLED_PRINT_SERVICES, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, Settings.Secure.DISPLAY_DENSITY_FORCED, + Settings.Secure.DOCKED_CLOCK_FACE, Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P 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/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 7f104b1b0a14..f27f3f9ca427 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -30,6 +30,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -41,6 +42,7 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -651,6 +653,59 @@ public class ChooserActivityTest { onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); } + @Test + public void contentProviderThrowSecurityException() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + sOverrides.resolverForceException = true; + + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + + @Test + public void contentProviderReturnsNoColumns() throws InterruptedException { + Uri uri = Uri.parse("content://com.android.frameworks.coretests/app.pdf"); + + ArrayList<Uri> uris = new ArrayList<>(); + uris.add(uri); + uris.add(uri); + + Intent sendIntent = createSendUriIntentWithPreview(uris); + + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + + Cursor cursor = mock(Cursor.class); + when(cursor.getCount()).thenReturn(1); + Mockito.doNothing().when(cursor).close(); + when(cursor.moveToFirst()).thenReturn(true); + when(cursor.getColumnIndex(Mockito.anyString())).thenReturn(-1); + + sOverrides.resolverCursor = cursor; + + mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); + waitForIdle(); + onView(withId(R.id.content_preview_filename)).check(matches(isDisplayed())); + onView(withId(R.id.content_preview_filename)).check(matches(withText("app.pdf + 1 file"))); + onView(withId(R.id.content_preview_file_icon)).check(matches(isDisplayed())); + } + private Intent createSendTextIntent() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 096b78b95fbd..57c84ff5c8ac 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -19,8 +19,10 @@ package com.android.internal.app; import static org.mockito.Mockito.mock; import android.app.usage.UsageStatsManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; +import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.util.Size; @@ -97,6 +99,19 @@ public class ChooserWrapperActivity extends ChooserActivity { return sOverrides.metricsLogger; } + @Override + public Cursor queryResolver(ContentResolver resolver, Uri uri) { + if (sOverrides.resolverCursor != null) { + return sOverrides.resolverCursor; + } + + if (sOverrides.resolverForceException) { + throw new SecurityException("Test exception handling"); + } + + return super.queryResolver(resolver, uri); + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -109,6 +124,8 @@ public class ChooserWrapperActivity extends ChooserActivity { public ResolverListController resolverListController; public Boolean isVoiceInteraction; public boolean isImageType; + public Cursor resolverCursor; + public boolean resolverForceException; public Bitmap previewThumbnail; public MetricsLogger metricsLogger; @@ -118,6 +135,8 @@ public class ChooserWrapperActivity extends ChooserActivity { createPackageManager = null; previewThumbnail = null; isImageType = false; + resolverCursor = null; + resolverForceException = false; resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 738221340aba..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"/> @@ -328,4 +328,9 @@ applications that come with the platform <permission name="android.permission.CONTROL_VPN"/> </privapp-permissions> + <privapp-permissions package="com.android.dynandroid"> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.MANAGE_DYNAMIC_ANDROID"/> + </privapp-permissions> + </permissions> 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..b020556cd15f 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); } @@ -945,6 +1027,18 @@ public class HardwareRenderer { */ public static native void disableVsync(); + /** + * Start render thread and initialize EGL or Vulkan. + * + * Initializing EGL involves loading and initializing the graphics driver. Some drivers take + * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render + * its first frame adds directly to user-visible app launch latency. + * + * Should only be called after GraphicsEnvironment.chooseDriver(). + * @hide + */ + public static native void preload(); + /** @hide */ protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile); 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/Typeface.java b/graphics/java/android/graphics/Typeface.java index ef9255f66695..e93e757410e0 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -128,7 +128,8 @@ public class Typeface { // Following two fields are not used but left for hiddenapi private list /** - * Use {@link SystemFonts#getAvailableFonts()} instead. + * sSystemFontMap is read only and unmodifiable. + * Use public API {@link #create(String, int)} to get the typeface for given familyName. */ @UnsupportedAppUsage(trackingBug = 123769347) static final Map<String, Typeface> sSystemFontMap; 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/Android.bp b/libs/hwui/Android.bp index 0335a7c6bd7d..793dd8d39376 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -318,6 +318,7 @@ cc_test { "tests/unit/RenderNodeDrawableTests.cpp", "tests/unit/RenderNodeTests.cpp", "tests/unit/RenderPropertiesTests.cpp", + "tests/unit/RenderThreadTests.cpp", "tests/unit/ShaderCacheTests.cpp", "tests/unit/SkiaBehaviorTests.cpp", "tests/unit/SkiaDisplayListTests.cpp", diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 76c56609ef47..2ffda839c210 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -107,7 +107,7 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { mRenderThread.requireGlContext(); } else { - mRenderThread.vulkanManager().initialize(); + mRenderThread.requireVkContext(); } if (!image.get()) { return CopyResult::UnknownError; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 15f53f286261..87cffb52c150 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -120,7 +120,7 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect } DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { - mVkManager.initialize(); + mRenderThread.requireVkContext(); return new DeferredLayerUpdater(mRenderThread.renderState()); } @@ -136,8 +136,9 @@ bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh setSurfaceColorProperties(colorMode); if (surface) { + mRenderThread.requireVkContext(); mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, - mSurfaceColorType); + mSurfaceColorType, mRenderThread.getGrContext()); } return mVkSurface != nullptr; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 720c60362a55..34f76d9b3579 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -381,6 +381,14 @@ void RenderProxy::releaseVDAtlasEntries() { }); } +void RenderProxy::preload() { + // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver. + auto& thread = RenderThread::getInstance(); + thread.queue().post([&thread]() { + thread.preload(); + }); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 6e1bfd74528a..a1a5551722bc 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -131,6 +131,8 @@ public: ANDROID_API static void disableVsync(); + ANDROID_API static void preload(); + static void repackVectorDrawableAtlas(); static void releaseVDAtlasEntries(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3904ed20fd77..08edd20c0a0d 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -41,6 +41,7 @@ #include <utils/Condition.h> #include <utils/Log.h> #include <utils/Mutex.h> +#include <thread> namespace android { namespace uirenderer { @@ -173,11 +174,8 @@ void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); - mVkManager = new VulkanManager(*this); + mVkManager = new VulkanManager(); mCacheManager = new CacheManager(mDisplayInfo); - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - mVkManager->initialize(); - } } void RenderThread::requireGlContext() { @@ -195,8 +193,7 @@ void RenderThread::requireGlContext() { LOG_ALWAYS_FATAL_IF(!glInterface.get()); GrContextOptions options; - options.fPreferExternalImagesOverES3 = true; - options.fDisableDistanceFieldPaths = true; + initGrContextOptions(options); auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION)); auto size = glesVersion ? strlen(glesVersion) : -1; cacheManager().configureContext(&options, glesVersion, size); @@ -205,6 +202,25 @@ void RenderThread::requireGlContext() { setGrContext(grContext); } +void RenderThread::requireVkContext() { + if (mVkManager->hasVkContext()) { + return; + } + mVkManager->initialize(); + GrContextOptions options; + initGrContextOptions(options); + // TODO: get a string describing the SPIR-V compiler version and use it here + cacheManager().configureContext(&options, nullptr, 0); + sk_sp<GrContext> grContext = mVkManager->createContext(options); + LOG_ALWAYS_FATAL_IF(!grContext.get()); + setGrContext(grContext); +} + +void RenderThread::initGrContextOptions(GrContextOptions& options) { + options.fPreferExternalImagesOverES3 = true; + options.fDisableDistanceFieldPaths = true; +} + void RenderThread::destroyRenderingContext() { mFunctorManager.onContextDestroyed(); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) { @@ -328,6 +344,7 @@ void RenderThread::requestVsync() { bool RenderThread::threadLoop() { setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY); + Looper::setForThread(mLooper); if (gOnStartHook) { gOnStartHook("RenderThread"); } @@ -390,6 +407,17 @@ bool RenderThread::isCurrent() { return gettid() == getInstance().getTid(); } +void RenderThread::preload() { + std::thread eglInitThread([]() { + //TODO: don't load EGL drivers for Vulkan, when HW bitmap uploader is refactored. + eglGetDisplay(EGL_DEFAULT_DISPLAY); + }); + eglInitThread.detach(); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + requireVkContext(); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index b18292820c6b..329b4b99c911 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -112,8 +112,11 @@ public: void dumpGraphicsMemory(int fd); void requireGlContext(); + void requireVkContext(); void destroyRenderingContext(); + void preload(); + /** * isCurrent provides a way to query, if the caller is running on * the render thread. @@ -122,6 +125,8 @@ public: */ static bool isCurrent(); + static void initGrContextOptions(GrContextOptions& options); + protected: virtual bool threadLoop() override; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 90397fddf618..3b43f1297597 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -55,11 +55,7 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} - void VulkanManager::destroy() { - mRenderThread.setGrContext(nullptr); - // We don't need to explicitly free the command buffer since it automatically gets freed when we // delete the VkCommandPool below. mDummyCB = VK_NULL_HANDLE; @@ -333,29 +329,10 @@ void VulkanManager::initialize() { LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); - GrVkExtensions extensions; - this->setupDevice(extensions, mPhysicalDeviceFeatures2); + this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); - auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { - if (device != VK_NULL_HANDLE) { - return vkGetDeviceProcAddr(device, proc_name); - } - return vkGetInstanceProcAddr(instance, proc_name); - }; - - GrVkBackendContext backendContext; - backendContext.fInstance = mInstance; - backendContext.fPhysicalDevice = mPhysicalDevice; - backendContext.fDevice = mDevice; - backendContext.fQueue = mGraphicsQueue; - backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; - backendContext.fMaxAPIVersion = mAPIVersion; - backendContext.fVkExtensions = &extensions; - backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; - backendContext.fGetProc = std::move(getProc); - // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { VkCommandPoolCreateInfo commandPoolInfo; @@ -376,22 +353,35 @@ void VulkanManager::initialize() { } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); - mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); - GrContextOptions options; - options.fDisableDistanceFieldPaths = true; - // TODO: get a string describing the SPIR-V compiler version and use it here - mRenderThread.cacheManager().configureContext(&options, nullptr, 0); - sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options)); - LOG_ALWAYS_FATAL_IF(!grContext.get()); - mRenderThread.setGrContext(grContext); - if (Properties::enablePartialUpdates && Properties::useBufferAge) { mSwapBehavior = SwapBehavior::BufferAge; } } +sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) { + auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); + }; + + GrVkBackendContext backendContext; + backendContext.fInstance = mInstance; + backendContext.fPhysicalDevice = mPhysicalDevice; + backendContext.fDevice = mDevice; + backendContext.fQueue = mGraphicsQueue; + backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; + backendContext.fMaxAPIVersion = mAPIVersion; + backendContext.fVkExtensions = &mExtensions; + backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; + backendContext.fGetProc = std::move(getProc); + + return GrContext::MakeVulkan(backendContext, options); +} + VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { return VkFunctorInitParams{ .instance = mInstance, @@ -470,8 +460,9 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { ColorMode colorMode = surface->mColorMode; sk_sp<SkColorSpace> colorSpace = surface->mColorSpace; SkColorType colorType = surface->mColorType; + GrContext* grContext = surface->mGrContext; destroySurface(surface); - *surfaceOut = createSurface(window, colorMode, colorSpace, colorType); + *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext); surface = *surfaceOut; if (!surface) { return nullptr; @@ -650,7 +641,7 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget( - mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, + surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin, surface->mColorType, surface->mColorSpace, &props); } @@ -880,15 +871,15 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType) { - initialize(); - + SkColorType surfaceColorType, + GrContext* grContext) { + LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized"); if (!window) { return nullptr; } VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace, - surfaceColorType); + surfaceColorType, grContext); VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 1fe6c65b35b8..95c9630fb728 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -22,6 +22,8 @@ #endif #include <vulkan/vulkan.h> +#include <GrContextOptions.h> +#include <vk/GrVkExtensions.h> #include <SkSurface.h> #include <ui/Fence.h> #include <utils/StrongPointer.h> @@ -39,9 +41,9 @@ class RenderThread; class VulkanSurface { public: VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace, - SkColorType colorType) + SkColorType colorType, GrContext* grContext) : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace), - mColorType(colorType) {} + mColorType(colorType), mGrContext(grContext) {} sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; } @@ -93,6 +95,7 @@ private: SkColorType mColorType; VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; SkMatrix mPreTransform; + GrContext* mGrContext; }; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, @@ -100,6 +103,9 @@ private: // windowing contexts. The VulkanManager must be initialized before use. class VulkanManager { public: + explicit VulkanManager() {} + ~VulkanManager() { destroy(); } + // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply // return. @@ -112,7 +118,8 @@ public: // VulkanSurface object which is returned. VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType); + SkColorType surfaceColorType, + GrContext* grContext); // Destroy the VulkanSurface and all associated vulkan objects. void destroySurface(VulkanSurface* surface); @@ -143,12 +150,9 @@ public: // Returned pointers are owned by VulkanManager. VkFunctorInitParams getVkFunctorInitParams() const; -private: - friend class RenderThread; - - explicit VulkanManager(RenderThread& thread); - ~VulkanManager() { destroy(); } + sk_sp<GrContext> createContext(const GrContextOptions& options); +private: // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); @@ -231,8 +235,6 @@ private: VkPtr<PFN_vkWaitForFences> mWaitForFences; VkPtr<PFN_vkResetFences> mResetFences; - RenderThread& mRenderThread; - VkInstance mInstance = VK_NULL_HANDLE; VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; VkDevice mDevice = VK_NULL_HANDLE; @@ -256,6 +258,7 @@ private: BufferAge, }; SwapBehavior mSwapBehavior = SwapBehavior::Discard; + GrVkExtensions mExtensions; }; } /* namespace renderthread */ diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index a9f651d38a06..e8ba15fe92af 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -100,7 +100,7 @@ void TestUtils::TestTask::run() { // RenderState only valid once RenderThread is running, so queried here renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance(); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - renderThread.vulkanManager().initialize(); + renderThread.requireVkContext(); } else { renderThread.requireGlContext(); } diff --git a/libs/hwui/tests/unit/RenderThreadTests.cpp b/libs/hwui/tests/unit/RenderThreadTests.cpp new file mode 100644 index 000000000000..af8ae7841af2 --- /dev/null +++ b/libs/hwui/tests/unit/RenderThreadTests.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "tests/common/TestUtils.h" +#include <utils/Looper.h> + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +RENDERTHREAD_TEST(RenderThread, isLooper) { + ASSERT_TRUE(Looper::getForThread() != nullptr); +} + 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/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index d742cc34b57e..733b866d9c4c 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -115,10 +115,14 @@ PointerController::~PointerController() { mLocked.pointerSprite.clear(); - for (size_t i = 0; i < mLocked.spots.size(); i++) { - delete mLocked.spots.itemAt(i); + for (auto& it : mLocked.spotsByDisplay) { + const std::vector<Spot*>& spots = it.second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots; i++) { + delete spots[i]; + } } - mLocked.spots.clear(); + mLocked.spotsByDisplay.clear(); mLocked.recycledSprites.clear(); } @@ -271,22 +275,30 @@ void PointerController::setPresentation(Presentation presentation) { } void PointerController::setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { + const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { #if DEBUG_POINTER_UPDATES ALOGD("setSpots: idBits=%08x", spotIdBits.value); for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, + ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), - c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + displayId); } #endif AutoMutex _l(mLock); + std::vector<Spot*> newSpots; + std::map<int32_t, std::vector<Spot*>>::const_iterator iter = + mLocked.spotsByDisplay.find(displayId); + if (iter != mLocked.spotsByDisplay.end()) { + newSpots = iter->second; + } + mSpriteController->openTransaction(); // Add or move spots for fingers that are down. @@ -298,17 +310,17 @@ void PointerController::setSpots(const PointerCoords* spotCoords, float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); - Spot* spot = getSpotLocked(id); + Spot* spot = getSpot(id, newSpots); if (!spot) { - spot = createAndAddSpotLocked(id); + spot = createAndAddSpotLocked(id, newSpots); } - spot->updateSprite(&icon, x, y); + spot->updateSprite(&icon, x, y, displayId); } // Remove spots for fingers that went up. - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); + for (size_t i = 0; i < newSpots.size(); i++) { + Spot* spot = newSpots[i]; if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { fadeOutAndReleaseSpotLocked(spot); @@ -316,6 +328,7 @@ void PointerController::setSpots(const PointerCoords* spotCoords, } mSpriteController->closeTransaction(); + mLocked.spotsByDisplay[displayId] = newSpots; } void PointerController::clearSpots() { @@ -539,21 +552,33 @@ bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { } // Animate spots that are fading out and being removed. - for (size_t i = 0; i < mLocked.spots.size();) { - Spot* spot = mLocked.spots.itemAt(i); - if (spot->id == Spot::INVALID_ID) { - spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; - if (spot->alpha <= 0) { - mLocked.spots.removeAt(i); - releaseSpotLocked(spot); - continue; - } else { - spot->sprite->setAlpha(spot->alpha); - keepAnimating = true; + for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) { + std::vector<Spot*>& spots = it->second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots;) { + Spot* spot = spots[i]; + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + spots.erase(spots.begin() + i); + releaseSpotLocked(spot); + numSpots--; + continue; + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } } + ++i; + } + + if (spots.size() == 0) { + it = mLocked.spotsByDisplay.erase(it); + } else { + ++it; } - ++i; } + return keepAnimating; } @@ -655,47 +680,49 @@ void PointerController::updatePointerLocked() REQUIRES(mLock) { mSpriteController->closeTransaction(); } -PointerController::Spot* PointerController::getSpotLocked(uint32_t id) { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); +PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; if (spot->id == id) { return spot; } } - return NULL; + + return nullptr; } -PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) { +PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id, + std::vector<Spot*>& spots) { // Remove spots until we have fewer than MAX_SPOTS remaining. - while (mLocked.spots.size() >= MAX_SPOTS) { - Spot* spot = removeFirstFadingSpotLocked(); + while (spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(spots); if (!spot) { - spot = mLocked.spots.itemAt(0); - mLocked.spots.removeAt(0); + spot = spots[0]; + spots.erase(spots.begin()); } releaseSpotLocked(spot); } // Obtain a sprite from the recycled pool. sp<Sprite> sprite; - if (! mLocked.recycledSprites.isEmpty()) { - sprite = mLocked.recycledSprites.top(); - mLocked.recycledSprites.pop(); + if (! mLocked.recycledSprites.empty()) { + sprite = mLocked.recycledSprites.back(); + mLocked.recycledSprites.pop_back(); } else { sprite = mSpriteController->createSprite(); } // Return the new spot. Spot* spot = new Spot(id, sprite); - mLocked.spots.push(spot); + spots.push_back(spot); return spot; } -PointerController::Spot* PointerController::removeFirstFadingSpotLocked() { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); +PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; if (spot->id == Spot::INVALID_ID) { - mLocked.spots.removeAt(i); + spots.erase(spots.begin() + i); return spot; } } @@ -706,7 +733,7 @@ void PointerController::releaseSpotLocked(Spot* spot) { spot->sprite->clearIcon(); if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { - mLocked.recycledSprites.push(spot->sprite); + mLocked.recycledSprites.push_back(spot->sprite); } delete spot; @@ -720,9 +747,13 @@ void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { } void PointerController::fadeOutAndReleaseAllSpotsLocked() { - for (size_t i = 0; i < mLocked.spots.size(); i++) { - Spot* spot = mLocked.spots.itemAt(i); - fadeOutAndReleaseSpotLocked(spot); + for (auto& it : mLocked.spotsByDisplay) { + const std::vector<Spot*>& spots = it.second; + size_t numSpots = spots.size(); + for (size_t i = 0; i < numSpots; i++) { + Spot* spot = spots[i]; + fadeOutAndReleaseSpotLocked(spot); + } } } @@ -743,12 +774,13 @@ void PointerController::loadResourcesLocked() REQUIRES(mLock) { // --- PointerController::Spot --- -void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) { +void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); sprite->setAlpha(alpha); sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); sprite->setPosition(x, y); - + sprite->setDisplayId(displayId); this->x = x; this->y = y; diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index be057867890d..52305b8244a6 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -103,7 +103,7 @@ public: virtual void setPresentation(Presentation presentation); virtual void setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits); + const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId); virtual void clearSpots(); void updatePointerIcon(int32_t iconId); @@ -133,7 +133,7 @@ private: : id(id), sprite(sprite), alpha(1.0f), scale(1.0f), x(0.0f), y(0.0f), lastIcon(NULL) { } - void updateSprite(const SpriteIcon* icon, float x, float y); + void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); private: const SpriteIcon* lastIcon; @@ -180,8 +180,8 @@ private: int32_t buttonState; - Vector<Spot*> spots; - Vector<sp<Sprite> > recycledSprites; + std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; + std::vector<sp<Sprite> > recycledSprites; } mLocked GUARDED_BY(mLock); bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -200,9 +200,9 @@ private: void removeInactivityTimeoutLocked(); void updatePointerLocked(); - Spot* getSpotLocked(uint32_t id); - Spot* createAndAddSpotLocked(uint32_t id); - Spot* removeFirstFadingSpotLocked(); + Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); + Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); + Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); void releaseSpotLocked(Spot* spot); void fadeOutAndReleaseSpotLocked(Spot* spot); void fadeOutAndReleaseAllSpotsLocked(); 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/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 0caa0c5b377a..b3953fddb622 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -184,8 +184,7 @@ public final class LocationRequest implements Parcelable { * @return a new location request */ public static LocationRequest create() { - LocationRequest request = new LocationRequest(); - return request; + return new LocationRequest(); } /** @hide */ @@ -230,12 +229,10 @@ public final class LocationRequest implements Parcelable { quality = ACCURACY_FINE; break; default: { - switch (criteria.getPowerRequirement()) { - case Criteria.POWER_HIGH: - quality = POWER_HIGH; - break; - default: - quality = POWER_LOW; + if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) { + quality = POWER_HIGH; + } else { + quality = POWER_LOW; } } } @@ -288,7 +285,7 @@ public final class LocationRequest implements Parcelable { * * @param quality an accuracy or power constant * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the quality constant is not valid + * @throws IllegalArgumentException if the quality constant is not valid */ public LocationRequest setQuality(int quality) { checkQuality(quality); @@ -331,7 +328,7 @@ public final class LocationRequest implements Parcelable { * * @param millis desired interval in millisecond, inexact * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the interval is less than zero + * @throws IllegalArgumentException if the interval is less than zero */ public LocationRequest setInterval(long millis) { checkInterval(millis); @@ -433,7 +430,7 @@ public final class LocationRequest implements Parcelable { * * @param millis fastest interval for updates in milliseconds, exact * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if the interval is less than zero + * @throws IllegalArgumentException if the interval is less than zero */ public LocationRequest setFastestInterval(long millis) { checkInterval(millis); @@ -528,7 +525,7 @@ public final class LocationRequest implements Parcelable { * * @param numUpdates the number of location updates requested * @return the same object, so that setters can be chained - * @throws InvalidArgumentException if numUpdates is 0 or less + * @throws IllegalArgumentException if numUpdates is 0 or less */ public LocationRequest setNumUpdates(int numUpdates) { if (numUpdates <= 0) { @@ -668,7 +665,7 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private static void checkProvider(String name) { if (name == null) { - throw new IllegalArgumentException("invalid provider: " + name); + throw new IllegalArgumentException("invalid provider: null"); } } @@ -758,9 +755,11 @@ public final class LocationRequest implements Parcelable { if (mNumUpdates != Integer.MAX_VALUE) { s.append(" num=").append(mNumUpdates); } - s.append(" lowPowerMode=").append(mLowPowerMode); + if (mLowPowerMode) { + s.append(" lowPowerMode"); + } if (mLocationSettingsIgnored) { - s.append(" ignoreSettings"); + s.append(" locationSettingsIgnored"); } s.append(']'); return s.toString(); diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java index a45c20d9d09d..af8123ac52f4 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -40,7 +40,7 @@ public final class ProviderRequest implements Parcelable { * restrictions or any other restricting factors and always satisfy this request to the best of * their ability. This flag should only be used in event of an emergency. */ - public boolean forceLocation = false; + public boolean locationSettingsIgnored = false; /** * Whether provider shall make stronger than normal tradeoffs to substantially restrict power @@ -70,6 +70,7 @@ public final class ProviderRequest implements Parcelable { request.reportLocation = in.readInt() == 1; request.interval = in.readLong(); request.lowPowerMode = in.readBoolean(); + request.locationSettingsIgnored = in.readBoolean(); int count = in.readInt(); for (int i = 0; i < count; i++) { request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in)); @@ -93,6 +94,7 @@ public final class ProviderRequest implements Parcelable { parcel.writeInt(reportLocation ? 1 : 0); parcel.writeLong(interval); parcel.writeBoolean(lowPowerMode); + parcel.writeBoolean(locationSettingsIgnored); parcel.writeInt(locationRequests.size()); for (LocationRequest request : locationRequests) { request.writeToParcel(parcel, flags); @@ -107,7 +109,12 @@ public final class ProviderRequest implements Parcelable { s.append("ON"); s.append(" interval="); TimeUtils.formatDuration(interval, s); - s.append(" lowPowerMode=" + lowPowerMode); + if (lowPowerMode) { + s.append(" lowPowerMode"); + } + if (locationSettingsIgnored) { + s.append(" locationSettingsIgnored"); + } } else { s.append("OFF"); } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index 67d64965ac96..dbb581fe54b2 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -31,6 +31,7 @@ package com.android.location.provider { method public long getInterval(); method public int getQuality(); method public float getSmallestDisplacement(); + method public boolean isLocationSettingsIgnored(); field public static final int ACCURACY_BLOCK = 102; // 0x66 field public static final int ACCURACY_CITY = 104; // 0x68 field public static final int ACCURACY_FINE = 100; // 0x64 @@ -44,10 +45,10 @@ package com.android.location.provider { } public final class ProviderRequestUnbundled { - method public boolean getForceLocation(); method public long getInterval(); method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests(); method public boolean getReportLocation(); + method public boolean isLocationSettingsIgnored(); } } diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java index 41fd769b5abd..2511c39caf5c 100644 --- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java @@ -121,6 +121,15 @@ public final class LocationRequestUnbundled { return delegate.getSmallestDisplacement(); } + /** + * Returns true if location settings will be ignored in order to satisfy this request. + * + * @return true if location settings will be ignored in order to satisfy this request + */ + public boolean isLocationSettingsIgnored() { + return delegate.isLocationSettingsIgnored(); + } + @Override public String toString() { return delegate.toString(); diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java index b825b58cd3e9..febbf1b23e0c 100644 --- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java @@ -46,15 +46,15 @@ public final class ProviderRequestUnbundled { return mRequest.interval; } - public boolean getForceLocation() { - return mRequest.forceLocation; + public boolean isLocationSettingsIgnored() { + return mRequest.locationSettingsIgnored; } /** * Never null. */ public List<LocationRequestUnbundled> getLocationRequests() { - List<LocationRequestUnbundled> result = new ArrayList<LocationRequestUnbundled>( + List<LocationRequestUnbundled> result = new ArrayList<>( mRequest.locationRequests.size()); for (LocationRequest r : mRequest.locationRequests) { result.add(new LocationRequestUnbundled(r)); diff --git a/media/OWNERS b/media/OWNERS index 03b751c07c6c..eb26367d3d29 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -11,3 +11,6 @@ lajos@google.com marcone@google.com sungsoo@google.com wjia@google.com + +# For maintaining sync with AndroidX code +per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java index 887b4475a4d1..4ea384ac70b2 100644 --- a/media/apex/java/android/media/MediaController2.java +++ b/media/apex/java/android/media/MediaController2.java @@ -317,7 +317,9 @@ public class MediaController2 implements AutoCloseable { isCanceled = !mRequestedCommandSeqNumbers.remove(seq); } if (isCanceled) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); + if (resultReceiver != null) { + resultReceiver.send(RESULT_INFO_SKIPPED, null); + } return; } Session2Command.Result result = mCallback.onSessionCommand( @@ -425,7 +427,7 @@ public class MediaController2 implements AutoCloseable { public void onDisconnected(@NonNull MediaController2 controller) {} /** - * Called when the playback of the session's playback activeness is changed. + * Called when the session's playback activeness is changed. * * @param controller the controller for this event * @param playbackActive {@code true} if the session's playback is active. diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java index fdd07fdd52e3..4c6945ae8d3f 100644 --- a/media/apex/java/android/media/MediaSession2.java +++ b/media/apex/java/android/media/MediaSession2.java @@ -259,6 +259,20 @@ public class MediaSession2 implements AutoCloseable { } } + /** + * Gets the list of the connected controllers + * + * @return list of the connected controllers. + */ + @NonNull + public List<ControllerInfo> getConnectedControllers() { + List<ControllerInfo> controllers = new ArrayList<>(); + synchronized (mLock) { + controllers.addAll(mConnectedControllers.values()); + } + return controllers; + } + boolean isClosed() { synchronized (mLock) { return mClosed; @@ -317,13 +331,6 @@ public class MediaSession2 implements AutoCloseable { if (DEBUG) { Log.d(TAG, "Accepting connection: " + controllerInfo); } - synchronized (mLock) { - if (mConnectedControllers.containsKey(controller)) { - Log.w(TAG, "Controller " + controllerInfo + " has sent connection" - + " request multiple times"); - } - mConnectedControllers.put(controller, controllerInfo); - } // If connection is accepted, notify the current state to the controller. // It's needed because we cannot call synchronous calls between // session/controller. @@ -339,6 +346,14 @@ public class MediaSession2 implements AutoCloseable { return; } controllerInfo.notifyConnected(connectionResult); + synchronized (mLock) { + if (mConnectedControllers.containsKey(controller)) { + Log.w(TAG, "Controller " + controllerInfo + " has sent connection" + + " request multiple times"); + } + mConnectedControllers.put(controller, controllerInfo); + } + mCallback.onPostConnect(MediaSession2.this, controllerInfo); connected = true; } finally { if (!connected) { @@ -417,14 +432,6 @@ public class MediaSession2 implements AutoCloseable { controllerInfo.removeRequestedCommandSeqNumber(seq); } - private List<ControllerInfo> getConnectedControllers() { - List<ControllerInfo> controllers = new ArrayList<>(); - synchronized (mLock) { - controllers.addAll(mConnectedControllers.values()); - } - return controllers; - } - /** * Builder for {@link MediaSession2}. * <p> @@ -738,6 +745,17 @@ public class MediaSession2 implements AutoCloseable { } /** + * Called immediately after a controller is connected. This is a convenient method to add + * custom initialization between the session and a controller. + * + * @param session the session for this event + * @param controller controller information. + */ + public void onPostConnect(@NonNull MediaSession2 session, + @NonNull ControllerInfo controller) { + } + + /** * Called when a controller is disconnected * * @param session the session for this event 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/HwAudioSource.java b/media/java/android/media/HwAudioSource.java new file mode 100644 index 000000000000..8bdb8a63f470 --- /dev/null +++ b/media/java/android/media/HwAudioSource.java @@ -0,0 +1,228 @@ +/* + * 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.media; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.util.Preconditions; + +/** + * The HwAudioSource represents the audio playback directly from a source audio device. + * It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only + * corresponding to {@link AudioSystem#startAudioSource(AudioPortConfig, AudioAttributes)} + * and {@link AudioSystem#stopAudioSource(int)}. + * + * @hide + */ +@SystemApi +public class HwAudioSource extends PlayerBase { + private final AudioDeviceInfo mAudioDeviceInfo; + private final AudioAttributes mAudioAttributes; + + private int mNativeHandle; + + /** + * Class constructor for a hardware audio source based player. + * + * Use the {@link Builder} class to construct a {@link HwAudioSource} instance. + * + * @param device {@link AudioDeviceInfo} instance of the source audio device. + * @param attributes {@link AudioAttributes} instance for this player. + */ + private HwAudioSource(@NonNull AudioDeviceInfo device, @NonNull AudioAttributes attributes) { + super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_HW_SOURCE); + Preconditions.checkNotNull(device); + Preconditions.checkNotNull(attributes); + Preconditions.checkArgument(device.isSource(), "Requires a source device"); + mAudioDeviceInfo = device; + mAudioAttributes = attributes; + baseRegisterPlayer(); + } + + /** + * TODO: sets the gain on {@link #mAudioDeviceInfo}. + * + * @param muting if true, the player is to be muted, and the volume values can be ignored + * @param leftVolume the left volume to use if muting is false + * @param rightVolume the right volume to use if muting is false + */ + @Override + void playerSetVolume(boolean muting, float leftVolume, float rightVolume) { + } + + /** + * TODO: applies {@link VolumeShaper} on {@link #mAudioDeviceInfo}. + * + * @param configuration a {@code VolumeShaper.Configuration} object + * created by {@link VolumeShaper.Configuration.Builder} or + * an created from a {@code VolumeShaper} id + * by the {@link VolumeShaper.Configuration} constructor. + * @param operation a {@code VolumeShaper.Operation}. + * @return + */ + @Override + int playerApplyVolumeShaper( + @NonNull VolumeShaper.Configuration configuration, + @NonNull VolumeShaper.Operation operation) { + return 0; + } + + /** + * TODO: gets the {@link VolumeShaper} by a given id. + * + * @param id the {@code VolumeShaper} id returned from + * sending a fully specified {@code VolumeShaper.Configuration} + * through {@link #playerApplyVolumeShaper} + * @return + */ + @Override + @Nullable + VolumeShaper.State playerGetVolumeShaperState(int id) { + return new VolumeShaper.State(1f, 1f); + } + + /** + * TODO: sets the level on {@link #mAudioDeviceInfo}. + * + * @param muting + * @param level + * @return + */ + @Override + int playerSetAuxEffectSendLevel(boolean muting, float level) { + return AudioSystem.SUCCESS; + } + + @Override + void playerStart() { + start(); + } + + @Override + void playerPause() { + // Pause is equivalent to stop for hardware audio source based players. + stop(); + } + + @Override + void playerStop() { + stop(); + } + + /** + * Starts the playback from {@link AudioDeviceInfo}. + */ + public void start() { + baseStart(); + mNativeHandle = AudioSystem.startAudioSource( + mAudioDeviceInfo.getPort().activeConfig(), + mAudioAttributes); + } + + /** + * Stops the playback from {@link AudioDeviceInfo}. + */ + public void stop() { + baseStop(); + if (mNativeHandle > 0) { + AudioSystem.stopAudioSource(mNativeHandle); + mNativeHandle = 0; + } + } + + /** + * Builder class for {@link HwAudioSource} objects. + * Use this class to configure and create a <code>HwAudioSource</code> instance. + * <p>Here is an example where <code>Builder</code> is used to specify an audio + * playback directly from a source device as media usage, to be used by a new + * <code>HwAudioSource</code> instance: + * + * <pre class="prettyprint"> + * HwAudioSource player = new HwAudioSource.Builder() + * .setAudioAttributes(new AudioAttributes.Builder() + * .setUsage(AudioAttributes.USAGE_MEDIA) + * .build()) + * .setAudioDeviceInfo(device) + * .build() + * </pre> + * <p> + * If the audio attributes are not set with {@link #setAudioAttributes(AudioAttributes)}, + * attributes comprising {@link AudioAttributes#USAGE_MEDIA} will be used. + */ + public static class Builder { + private AudioAttributes mAudioAttributes; + private AudioDeviceInfo mAudioDeviceInfo; + + /** + * Constructs a new Builder with default values. + */ + public Builder() { + } + + /** + * Sets the {@link AudioAttributes}. + * @param attributes a non-null {@link AudioAttributes} instance that describes the audio + * data to be played. + * @return the same Builder instance. + */ + public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) { + Preconditions.checkNotNull(attributes); + mAudioAttributes = attributes; + return this; + } + + /** + * Sets the {@link AudioDeviceInfo}. + * @param info a non-null {@link AudioDeviceInfo} instance that describes the audio + * data come from. + * @return the same Builder instance. + */ + public @NonNull Builder setAudioDeviceInfo(@NonNull AudioDeviceInfo info) { + Preconditions.checkNotNull(info); + Preconditions.checkArgument(info.isSource()); + mAudioDeviceInfo = info; + return this; + } + + /** + * Builds an {@link HwAudioSource} instance initialized with all the parameters set + * on this <code>Builder</code>. + * @return a new successfully initialized {@link HwAudioSource} instance. + */ + public @NonNull HwAudioSource build() { + Preconditions.checkNotNull(mAudioDeviceInfo); + if (mAudioAttributes == null) { + mAudioAttributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .build(); + } + return new HwAudioSource(mAudioDeviceInfo, mAudioAttributes); + } + } + + /** + * Eliminate {@link #deprecateStreamTypeForPlayback(int, String, String)} in API list. + * TODO: remove this pseudo-override function + * @hide + */ + public static void deprecateStreamTypeForPlayback(int streamType, String className, + String opName) throws IllegalArgumentException { + // Do nothing. + } +} 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/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index fb18c3b67480..e4d356b48f6d 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1180,13 +1180,8 @@ public class MediaPlayer extends PlayerBase } final File file = new File(path); - if (file.exists()) { - FileInputStream is = new FileInputStream(file); - FileDescriptor fd = is.getFD(); - setDataSource(fd); - is.close(); - } else { - throw new IOException("setDataSource failed."); + try (FileInputStream is = new FileInputStream(file)) { + setDataSource(is.getFD()); } } @@ -2868,15 +2863,9 @@ public class MediaPlayer extends PlayerBase throw new IllegalArgumentException(msg); } - File file = new File(path); - if (file.exists()) { - FileInputStream is = new FileInputStream(file); - FileDescriptor fd = is.getFD(); - addTimedTextSource(fd, mimeType); - is.close(); - } else { - // We do not support the case where the path is not a file. - throw new IOException(path); + final File file = new File(path); + try (FileInputStream is = new FileInputStream(file)) { + addTimedTextSource(is.getFD(), mimeType); } } 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/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index ccf49bd774a8..21b194d73ff9 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -122,6 +122,9 @@ public class ThumbnailUtils { * @param filePath The audio file. * @param kind The desired thumbnail kind, such as * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. + * @deprecated Callers should migrate to using + * {@link #createAudioThumbnail(File, Size, CancellationSignal)}, + * as it offers more control over resizing and cancellation. */ @Deprecated public static @Nullable Bitmap createAudioThumbnail(@NonNull String filePath, int kind) { @@ -211,6 +214,9 @@ public class ThumbnailUtils { * @param filePath The image file. * @param kind The desired thumbnail kind, such as * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. + * @deprecated Callers should migrate to using + * {@link #createImageThumbnail(File, Size, CancellationSignal)}, + * as it offers more control over resizing and cancellation. */ @Deprecated public static @Nullable Bitmap createImageThumbnail(@NonNull String filePath, int kind) { @@ -270,6 +276,9 @@ public class ThumbnailUtils { * @param filePath The video file. * @param kind The desired thumbnail kind, such as * {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND}. + * @deprecated Callers should migrate to using + * {@link #createVideoThumbnail(File, Size, CancellationSignal)}, + * as it offers more control over resizing and cancellation. */ @Deprecated public static @Nullable Bitmap createVideoThumbnail(@NonNull String filePath, int kind) { diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.aidl b/media/java/android/media/audiopolicy/AudioProductStrategies.aidl new file mode 100644 index 000000000000..bec11bcfab5d --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioProductStrategies.aidl @@ -0,0 +1,18 @@ +/* Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media.audiopolicy; + +parcelable AudioProductStrategies; diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java new file mode 100644 index 000000000000..b8364091ff12 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.audiopolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.media.AudioAttributes; +import android.media.AudioSystem; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @hide + * A class to encapsulate a collection of {@link AudioProductStrategy}. + * Provides helper functions to easily retrieve the {@link AudioAttributes} for a given product + * strategy or legacy stream type. + */ +@SystemApi +public final class AudioProductStrategies implements Iterable<AudioProductStrategy>, Parcelable { + + private final ArrayList<AudioProductStrategy> mAudioProductStrategyList; + + private static final String TAG = "AudioProductStrategies"; + + public AudioProductStrategies() { + ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>(); + int status = native_list_audio_product_strategies(apsList); + if (status != AudioSystem.SUCCESS) { + Log.w(TAG, ": createAudioProductStrategies failed"); + } + mAudioProductStrategyList = apsList; + } + + private AudioProductStrategies(ArrayList<AudioProductStrategy> audioProductStrategy) { + mAudioProductStrategyList = audioProductStrategy; + } + + /** + * @hide + * @return number of {@link AudioProductStrategy} objects + */ + @SystemApi + public int size() { + return mAudioProductStrategyList.size(); + } + + /** + * @hide + * @return the matching {@link AudioProductStrategy} objects with the given id, + * null object if not found. + */ + @SystemApi + public @Nullable AudioProductStrategy getById(int productStrategyId) { + for (final AudioProductStrategy avg : this) { + if (avg.getId() == productStrategyId) { + return avg; + } + } + Log.e(TAG, ": invalid product strategy id: " + productStrategyId + " requested"); + return null; + } + + /** + * Returns an {@link Iterator} + */ + @Override + public Iterator<AudioProductStrategy> iterator() { + return mAudioProductStrategyList.iterator(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioProductStrategies that = (AudioProductStrategies) o; + + return mAudioProductStrategyList.equals(that.mAudioProductStrategyList); + } + + /** + * @hide + * @param aps {@link AudioProductStrategy} (which is the generalisation of Car Audio Usage / + * legacy routing_strategy linked to {@link AudioAttributes#getUsage()} ) + * @return the {@link AudioAttributes} relevant for the given product strategy. + * If none is found, it builds the default attributes. + * TODO: shall the helper collection be able to identify the platform default? + */ + @SystemApi + @NonNull + public AudioAttributes getAudioAttributesForProductStrategy(@NonNull AudioProductStrategy aps) { + Preconditions.checkNotNull(aps, "AudioProductStrategy must not be null"); + for (final AudioProductStrategy audioProductStrategy : this) { + if (audioProductStrategy.equals(aps)) { + return audioProductStrategy.getAudioAttributes(); + } + } + return new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_UNKNOWN).build(); + } + + /** + * @hide + * @param streamType legacy stream type used for volume operation only + * @return the {@link AudioAttributes} relevant for the given streamType. + * If none is found, it builds the default attributes. + */ + @SystemApi + public @NonNull AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) { + for (final AudioProductStrategy productStrategy : this) { + AudioAttributes aa = productStrategy.getAudioAttributesForLegacyStreamType(streamType); + if (aa != null) { + return aa; + } + } + return new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) + .setUsage(AudioAttributes.USAGE_UNKNOWN).build(); + } + + /** + * @hide + * @param aa the {@link AudioAttributes} for which stream type is requested + * @return the legacy stream type relevant for the given {@link AudioAttributes}. + * If the product strategy is not associated to any stream, it returns STREAM_MUSIC. + * If no product strategy supports the stream type, it returns STREAM_MUSIC. + */ + @SystemApi + public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) { + Preconditions.checkNotNull(aa, "AudioAttributes must not be null"); + for (final AudioProductStrategy productStrategy : this) { + if (productStrategy.supportsAudioAttributes(aa)) { + int streamType = productStrategy.getLegacyStreamTypeForAudioAttributes(aa); + if (streamType == AudioSystem.STREAM_DEFAULT) { + Log.w(TAG, "Attributes " + aa.toString() + " ported by strategy " + + productStrategy.name() + " has no stream type associated, " + + "DO NOT USE STREAM TO CONTROL THE VOLUME"); + return AudioSystem.STREAM_MUSIC; + } + return streamType; + } + } + return AudioSystem.STREAM_MUSIC; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(size()); + for (final AudioProductStrategy productStrategy : this) { + productStrategy.writeToParcel(dest, flags); + } + } + + public static final Parcelable.Creator<AudioProductStrategies> CREATOR = + new Parcelable.Creator<AudioProductStrategies>() { + @Override + public AudioProductStrategies createFromParcel(@NonNull Parcel in) { + ArrayList<AudioProductStrategy> apsList = new ArrayList<AudioProductStrategy>(); + int size = in.readInt(); + for (int index = 0; index < size; index++) { + apsList.add(AudioProductStrategy.CREATOR.createFromParcel(in)); + } + return new AudioProductStrategies(apsList); + } + + @Override + public @NonNull AudioProductStrategies[] newArray(int size) { + return new AudioProductStrategies[size]; + } + }; + + private static native int native_list_audio_product_strategies( + ArrayList<AudioProductStrategy> strategies); +} diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.aidl b/media/java/android/media/audiopolicy/AudioProductStrategy.aidl new file mode 100644 index 000000000000..5ead30bccbcc --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.aidl @@ -0,0 +1,18 @@ +/* Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media.audiopolicy; + +parcelable AudioProductStrategy; diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java new file mode 100644 index 000000000000..af6e8bfdbd83 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.audiopolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.media.AudioAttributes; +import android.media.AudioSystem; +import android.media.MediaRecorder; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.internal.util.Preconditions; + +/** + * @hide + * A class to encapsulate a collection of attributes associated to a given product strategy + * (and for legacy reason, keep the association with the stream type). + */ +@SystemApi +public final class AudioProductStrategy implements Parcelable { + /** + * group value to use when introspection API fails. + * @hide + */ + public static final int DEFAULT_GROUP = -1; + + private final AudioAttributesGroup[] mAudioAttributesGroups; + private final String mName; + /** + * Unique identifier of a product strategy. + * This Id can be assimilated to Car Audio Usage and even more generally to usage. + * For legacy platforms, the product strategy id is the routing_strategy, which was hidden to + * upper layer but was transpiring in the {@link AudioAttributes#getUsage()}. + */ + private int mId; + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioProductStrategy thatStrategy = (AudioProductStrategy) o; + + return mName == thatStrategy.mName && mId == thatStrategy.mId + && mAudioAttributesGroups.equals(thatStrategy.mAudioAttributesGroups); + } + + /** + * @param name of the product strategy + * @param id of the product strategy + * @param audioAttributes {@link AudioAttributes} associated to the given product strategy + * @param legacyStreamTypes associated to the given product strategy. + */ + private AudioProductStrategy(@NonNull String name, int id, + @NonNull AudioAttributesGroup[] aag) { + Preconditions.checkNotNull(name, "name must not be null"); + Preconditions.checkNotNull(aag, "AudioAttributesGroups must not be null"); + mName = name; + mId = id; + mAudioAttributesGroups = aag; + } + + /** + * @hide + * @return human-readable name of this product strategy, which is similar to a usage + */ + @SystemApi + public @NonNull String name() { + return mName; + } + + /** + * @hide + * @return the product strategy ID (which is the generalisation of Car Audio Usage / legacy + * routing_strategy linked to {@link AudioAttributes#getUsage()}). + */ + @SystemApi + public int getId() { + return mId; + } + + /** + * @hide + * @return first {@link AudioAttributes} associated to this product strategy. + */ + @SystemApi + public @NonNull AudioAttributes getAudioAttributes() { + // We need a choice, so take the first one + return mAudioAttributesGroups.length == 0 ? (new AudioAttributes.Builder().build()) + : mAudioAttributesGroups[0].getAudioAttributes(); + } + + /** + * @hide + * @param streamType legacy stream type used for volume operation only + * @return the {@link AudioAttributes} relevant for the given streamType. + * If none is found, it builds the default attributes. + */ + public @Nullable AudioAttributes getAudioAttributesForLegacyStreamType(int streamType) { + for (final AudioAttributesGroup aag : mAudioAttributesGroups) { + if (aag.supportsStreamType(streamType)) { + return aag.getAudioAttributes(); + } + } + return null; + } + + /** + * @hide + * @param aa the {@link AudioAttributes} to be considered + * @return the legacy stream type relevant for the given {@link AudioAttributes}. + * If none is found, it return DEFAULT stream type. + */ + public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) { + Preconditions.checkNotNull(aa, "AudioAttributes must not be null"); + for (final AudioAttributesGroup aag : mAudioAttributesGroups) { + if (aag.supportsAttributes(aa)) { + return aag.getStreamType(); + } + } + return AudioSystem.STREAM_DEFAULT; + } + + /** + * @hide + * @param aa the {@link AudioAttributes} to be considered + * @return true if the {@link AudioProductStrategy} supports the given {@link AudioAttributes}, + * false otherwise. + */ + public boolean supportsAudioAttributes(@NonNull AudioAttributes aa) { + Preconditions.checkNotNull(aa, "AudioAttributes must not be null"); + for (final AudioAttributesGroup aag : mAudioAttributesGroups) { + if (aag.supportsAttributes(aa)) { + return true; + } + } + return false; + } + + /** + * @hide + * @param streamType legacy stream type used for volume operation only + * @return the {@link AudioAttributes} relevant for the given streamType. + * If none is found, it builds the default attributes. + */ + public int getGroupIdForLegacyStreamType(int streamType) { + for (final AudioAttributesGroup aag : mAudioAttributesGroups) { + if (aag.supportsStreamType(streamType)) { + return aag.getGroupId(); + } + } + return DEFAULT_GROUP; + } + + /** + * @hide + * @param aa the {@link AudioAttributes} to be considered + * @return the group id associated with the given audio attributes if found, + * default value otherwise. + */ + public int getGroupIdForAudioAttributes(@NonNull AudioAttributes aa) { + Preconditions.checkNotNull(aa, "AudioAttributes must not be null"); + for (final AudioAttributesGroup aag : mAudioAttributesGroups) { + if (aag.supportsAttributes(aa)) { + return aag.getGroupId(); + } + } + return DEFAULT_GROUP; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mId); + dest.writeInt(mAudioAttributesGroups.length); + for (AudioAttributesGroup aag : mAudioAttributesGroups) { + aag.writeToParcel(dest, flags); + } + } + + public static final Parcelable.Creator<AudioProductStrategy> CREATOR = + new Parcelable.Creator<AudioProductStrategy>() { + @Override + public AudioProductStrategy createFromParcel(@NonNull Parcel in) { + String name = in.readString(); + int id = in.readInt(); + int nbAttributesGroups = in.readInt(); + AudioAttributesGroup[] aag = new AudioAttributesGroup[nbAttributesGroups]; + for (int index = 0; index < nbAttributesGroups; index++) { + aag[index] = AudioAttributesGroup.CREATOR.createFromParcel(in); + } + return new AudioProductStrategy(name, id, aag); + } + + @Override + public @NonNull AudioProductStrategy[] newArray(int size) { + return new AudioProductStrategy[size]; + } + }; + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + s.append("\n Name: "); + s.append(mName); + s.append(" Id: "); + s.append(Integer.toString(mId)); + for (AudioAttributesGroup aag : mAudioAttributesGroups) { + s.append(aag.toString()); + } + return s.toString(); + } + + /** + * @hide + * Default attributes, with default source to be aligned with native. + */ + public static final @NonNull AudioAttributes sDefaultAttributes = + new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT) + .build(); + + /** + * To avoid duplicating the logic in java and native, we shall make use of + * native API native_get_product_strategies_from_audio_attributes + * @param refAttr {@link AudioAttributes} to be taken as the reference + * @param attr {@link AudioAttributes} of the requester. + */ + private static boolean attributesMatches(@NonNull AudioAttributes refAttr, + @NonNull AudioAttributes attr) { + Preconditions.checkNotNull(refAttr, "refAttr must not be null"); + Preconditions.checkNotNull(attr, "attr must not be null"); + String refFormattedTags = TextUtils.join(";", refAttr.getTags()); + String cliFormattedTags = TextUtils.join(";", attr.getTags()); + if (refAttr.equals(sDefaultAttributes)) { + return false; + } + return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN) + || (attr.getUsage() == refAttr.getUsage())) + && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN) + || (attr.getContentType() == refAttr.getContentType())) + && ((refAttr.getAllFlags() == 0) + || (attr.getAllFlags() != 0 + && (attr.getAllFlags() & refAttr.getAllFlags()) == attr.getAllFlags())) + && ((refFormattedTags.length() == 0) || refFormattedTags.equals(cliFormattedTags)); + } + + + private static final class AudioAttributesGroup implements Parcelable { + private int mGroupId; + private int mLegacyStreamType; + private final AudioAttributes[] mAudioAttributes; + + AudioAttributesGroup(int groupId, int streamType, + @NonNull AudioAttributes[] audioAttributes) { + mGroupId = groupId; + mLegacyStreamType = streamType; + mAudioAttributes = audioAttributes; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioAttributesGroup thatAag = (AudioAttributesGroup) o; + + return mGroupId == thatAag.mGroupId + && mLegacyStreamType == thatAag.mLegacyStreamType + && mAudioAttributes.equals(thatAag.mAudioAttributes); + } + + public int getStreamType() { + return mLegacyStreamType; + } + + public int getGroupId() { + return mGroupId; + } + + public @NonNull AudioAttributes getAudioAttributes() { + // We need a choice, so take the first one + return mAudioAttributes.length == 0 ? (new AudioAttributes.Builder().build()) + : mAudioAttributes[0]; + } + + /** + * Checks if a {@link AudioAttributes} is supported by this product strategy. + * @param {@link AudioAttributes} to check upon support + * @return true if the {@link AudioAttributes} follows this product strategy, + false otherwise. + */ + public boolean supportsAttributes(@NonNull AudioAttributes attributes) { + for (final AudioAttributes refAa : mAudioAttributes) { + if (refAa.equals(attributes) || attributesMatches(refAa, attributes)) { + return true; + } + } + return false; + } + + public boolean supportsStreamType(int streamType) { + return mLegacyStreamType == streamType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mGroupId); + dest.writeInt(mLegacyStreamType); + dest.writeInt(mAudioAttributes.length); + for (AudioAttributes attributes : mAudioAttributes) { + attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/); + } + } + + public static final Parcelable.Creator<AudioAttributesGroup> CREATOR = + new Parcelable.Creator<AudioAttributesGroup>() { + @Override + public AudioAttributesGroup createFromParcel(@NonNull Parcel in) { + int groupId = in.readInt(); + int streamType = in.readInt(); + int nbAttributes = in.readInt(); + AudioAttributes[] aa = new AudioAttributes[nbAttributes]; + for (int index = 0; index < nbAttributes; index++) { + aa[index] = AudioAttributes.CREATOR.createFromParcel(in); + } + return new AudioAttributesGroup(groupId, streamType, aa); + } + + @Override + public @NonNull AudioAttributesGroup[] newArray(int size) { + return new AudioAttributesGroup[size]; + } + }; + + + @Override + public @NonNull String toString() { + StringBuilder s = new StringBuilder(); + s.append("\n Legacy Stream Type: "); + s.append(Integer.toString(mLegacyStreamType)); + s.append(" Group Id: "); + s.append(Integer.toString(mGroupId)); + + for (AudioAttributes attribute : mAudioAttributes) { + s.append("\n -"); + s.append(attribute.toString()); + } + return s.toString(); + } + } +} 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/media/native/midi/Android.bp b/media/native/midi/Android.bp index d069bd2f6284..58317edbea68 100644 --- a/media/native/midi/Android.bp +++ b/media/native/midi/Android.bp @@ -16,7 +16,7 @@ cc_library_shared { name: "libamidi", srcs: [ - "midi.cpp", + "amidi.cpp", ":IMidiDeviceServer.aidl", ], @@ -48,10 +48,10 @@ ndk_headers { from: "include", - to: "amidi", + to: "", - srcs: ["include/midi.h"], - license: "include/NOTICE", + srcs: ["include/amidi/AMidi.h"], + license: "include/amidi/NOTICE", } ndk_library { diff --git a/media/native/midi/midi.cpp b/media/native/midi/amidi.cpp index a5bdba8569b8..1e9a194d76c8 100644 --- a/media/native/midi/midi.cpp +++ b/media/native/midi/amidi.cpp @@ -28,8 +28,8 @@ #include "android/media/midi/BpMidiDeviceServer.h" #include "media/MidiDeviceInfo.h" -#include "include/midi.h" -#include "midi_internal.h" +#include "include/amidi/AMidi.h" +#include "amidi_internal.h" using namespace android::media::midi; diff --git a/media/native/midi/midi_internal.h b/media/native/midi/amidi_internal.h index cb3ecce13533..fce85963d217 100644 --- a/media/native/midi/midi_internal.h +++ b/media/native/midi/amidi_internal.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_MEDIA_MIDI_INTERNAL_H_ -#define ANDROID_MEDIA_MIDI_INTERNAL_H_ +#ifndef ANDROID_MEDIA_AMIDI_INTERNAL_H_ +#define ANDROID_MEDIA_AMIDI_INTERNAL_H_ #include <jni.h> @@ -38,4 +38,4 @@ struct AMidiDevice { AMidiDeviceInfo deviceInfo; /* Attributes of the device. */ }; -#endif // ANDROID_MEDIA_MIDI_INTERNAL_H_ +#endif // ANDROID_MEDIA_AMIDI_INTERNAL_H_ diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/amidi/AMidi.h index 755d09fc4ff2..0d60b0dd63d4 100644 --- a/media/native/midi/include/midi.h +++ b/media/native/midi/include/amidi/AMidi.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_MEDIA_MIDI_H_ -#define ANDROID_MEDIA_MIDI_H_ +#ifndef ANDROID_MEDIA_AMIDI_H_ +#define ANDROID_MEDIA_AMIDI_H_ #include <stdarg.h> #include <stdint.h> @@ -66,9 +66,9 @@ enum { * @param outDevicePtrPtr Points to the pointer to receive the AMidiDevice * * @return AMEDIA_OK on success, or a negative error value: - * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - the midiDeviceObj + * @see AMEDIA_ERROR_INVALID_OBJECT - the midiDeviceObj * is null or already connected to a native AMidiDevice - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred. + * @see AMEDIA_ERROR_UNKNOWN - an unknown error occurred. */ media_status_t AMIDI_API AMidiDevice_fromJava( JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29); @@ -80,13 +80,10 @@ media_status_t AMIDI_API AMidiDevice_fromJava( * * @return AMEDIA_OK on success, * or a negative error value: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - * - the device parameter is NULL. - * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - * - the device is not consistent with the associated Java MidiDevice. - * @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - * - the JNI interface initialization to the associated java MidiDevice failed. - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info. + * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. + * @see AMEDIA_ERROR_INVALID_OBJECT - the device is not consistent with the associated Java MidiDevice. + * @see AMEDIA_ERROR_INVALID_OBJECT - the JNI interface initialization to the associated java MidiDevice failed. + * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29); @@ -100,9 +97,8 @@ media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __IN * AMIDI_DEVICE_TYPE_VIRTUAL * AMIDI_DEVICE_TYPE_BLUETOOTH * or a negative error value: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device - * parameter is NULL. - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error. + * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN - Unknown error. */ int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29); @@ -113,9 +109,8 @@ int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN * * @return If successful, returns the number of MIDI input (sending) ports available on the * device. If an error occurs, returns a negative value indicating the error: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device - * parameter is NULL. - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info. + * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); @@ -126,9 +121,8 @@ ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTR * * @return If successful, returns the number of MIDI output (receiving) ports available on the * device. If an error occurs, returns a negative value indicating the error: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device - * parameter is NULL. - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info. + * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. + * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); @@ -146,7 +140,7 @@ ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INT * @param outOutputPortPtr Receives the native API port identifier of the opened port. * * @return AMEDIA_OK, or a negative error code: - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29); @@ -174,7 +168,7 @@ void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTROD * (the current value of the running Java Virtual Machine's high-resolution time source, * in nanoseconds) * @return the number of messages received (either 0 or 1), or a negative error code: - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29); @@ -193,7 +187,7 @@ ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int * @param outInputPortPtr Receives the native API port identifier of the opened port. * * @return AMEDIA_OK, or a negative error code: - * @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error. + * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29); @@ -206,8 +200,7 @@ media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t * @param numBytes Specifies the number of bytes to write. * * @return The number of bytes sent, which could be less than specified or a negative error code: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port - * was NULL, the specified buffer was NULL. + * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL. */ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes) __INTRODUCED_IN(29); @@ -221,8 +214,7 @@ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uin * @param timestamp The CLOCK_MONOTONIC time in nanoseconds to associate with the sent data. * * @return The number of bytes sent, which could be less than specified or a negative error code: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port - * was NULL, the specified buffer was NULL. + * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL. */ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29); @@ -233,10 +225,9 @@ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPo * * @param inputPort The identifier of the port to send the flush command to. * - * @returns @see AMEDIA_OK {@link AMEDIA_OK} if successful, otherwise a negative error code: - * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port - * was NULL - * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't + * @returns @see AMEDIA_OK if successful, otherwise a negative error code: + * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL + * @see AMEDIA_ERROR_UNSUPPORTED - The FLUSH command couldn't * be sent. */ media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); @@ -252,4 +243,4 @@ void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCE } #endif -#endif /* ANDROID_MEDIA_MIDI_H_ */ +#endif /* ANDROID_MEDIA_AMIDI_H_ */ diff --git a/media/native/midi/include/NOTICE b/media/native/midi/include/amidi/NOTICE index e72ff94a8401..e72ff94a8401 100644 --- a/media/native/midi/include/NOTICE +++ b/media/native/midi/include/amidi/NOTICE diff --git a/packages/BackupRestoreConfirmation/Android.bp b/packages/BackupRestoreConfirmation/Android.bp new file mode 100644 index 000000000000..b0222da9405b --- /dev/null +++ b/packages/BackupRestoreConfirmation/Android.bp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "BackupRestoreConfirmation", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", + privileged: true, +} diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk deleted file mode 100644 index 532d272f70f3..000000000000 --- a/packages/BackupRestoreConfirmation/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := BackupRestoreConfirmation -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -######################## -include $(call all-makefiles-under,$(LOCAL_PATH)) - 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/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java index ca343d1d0bef..8077431e9da2 100644 --- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java +++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java @@ -24,13 +24,17 @@ import android.app.ActivityManager; import android.car.Car; import android.car.CarNotConnectedException; import android.car.drivingstate.CarUxRestrictionsManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.GestureDetector; @@ -91,12 +95,28 @@ public class NotificationsUI extends SystemUI private static int sSettleOpenPercentage; private static int sSettleClosePercentage; + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_USER_SWITCHED)) { + mCarNotificationListener.registerAsSystemService(mContext, + mCarUxRestrictionManagerWrapper, + mClickHandlerFactory); + inflateNotificationContent(); + } + } + }; + /** * Inits the window that hosts the notifications and establishes the connections * to the car related services. */ @Override public void start() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); sSettleOpenPercentage = mContext.getResources().getInteger( R.integer.notification_settle_open_percentage); sSettleClosePercentage = mContext.getResources().getInteger( @@ -117,8 +137,7 @@ public class NotificationsUI extends SystemUI closeCarNotifications(DEFAULT_FLING_VELOCITY); } }); - mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper, - mClickHandlerFactory); + mCar = Car.createCar(mContext, mCarConnectionListener); mCar.connect(); NotificationGestureListener gestureListener = new NotificationGestureListener(); @@ -131,8 +150,6 @@ public class NotificationsUI extends SystemUI mCarNotificationWindow .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color)); - inflateNotificationContent(); - WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY, diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index c5a951ca5249..369bb9fdd9dd 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -512,32 +512,52 @@ public class CarStatusBar extends StatusBar implements } @Override + public void setLockscreenUser(int newUserId) { + super.setLockscreenUser(newUserId); + // Try to dismiss the keyguard after every user switch. + dismissKeyguardWhenUserSwitcherNotDisplayed(); + } + + @Override public void onStateChanged(int newState) { super.onStateChanged(newState); startSwitchToGuestTimerIfDrivingOnKeyguard(); - if (mFullscreenUserSwitcher == null) { - return; // Not using the full screen user switcher. - } - - if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) { - if (!mFullscreenUserSwitcher.isVisible()) { - // Current execution path continues to set state after this, thus we deffer the - // dismissal to the next execution cycle. - postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible. - } + if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) { + hideUserSwitcher(); } else { - mFullscreenUserSwitcher.hide(); + dismissKeyguardWhenUserSwitcherNotDisplayed(); } } + /** Makes the full screen user switcher visible, if applicable. */ public void showUserSwitcher() { if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) { mFullscreenUserSwitcher.show(); // Makes the switcher visible. } } + private void hideUserSwitcher() { + if (mFullscreenUserSwitcher != null) { + mFullscreenUserSwitcher.hide(); + } + } + + // We automatically dismiss keyguard unless user switcher is being shown on the keyguard. + private void dismissKeyguardWhenUserSwitcherNotDisplayed() { + if (mFullscreenUserSwitcher == null) { + return; // Not using the full screen user switcher. + } + + if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER + && !mFullscreenUserSwitcher.isVisible()) { + // Current execution path continues to set state after this, thus we deffer the + // dismissal to the next execution cycle. + postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible. + } + } + public void postDismissKeyguard() { mHandler.post(this::dismissKeyguard); } diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp new file mode 100644 index 000000000000..c1b0b2da2cb5 --- /dev/null +++ b/packages/CarrierDefaultApp/Android.bp @@ -0,0 +1,6 @@ +android_app { + name: "CarrierDefaultApp", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", +} diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk deleted file mode 100644 index df88afdcf041..000000000000 --- a/packages/CarrierDefaultApp/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := CarrierDefaultApp -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) - -# This finds and builds the test apk as well, so a single make does both. -include $(call all-makefiles-under,$(LOCAL_PATH)) 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/CarrierDefaultApp/tests/Android.mk b/packages/CarrierDefaultApp/tests/Android.mk deleted file mode 100644 index 6ebb57586ff6..000000000000 --- a/packages/CarrierDefaultApp/tests/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2016, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_CERTIFICATE := platform - -# Include all makefiles in subdirectories -include $(call all-makefiles-under,$(LOCAL_PATH)) - - - - diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp new file mode 100644 index 000000000000..96144cf76ff7 --- /dev/null +++ b/packages/CarrierDefaultApp/tests/unit/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2016, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "CarrierDefaultAppUnitTests", + certificate: "platform", + libs: [ + "android.test.runner", + "telephony-common", + "android.test.base", + ], + static_libs: [ + "androidx.test.rules", + "mockito-target-minus-junit4", + ], + // Include all test java files. + srcs: ["src/**/*.java"], + platform_apis: true, + instrumentation_for: "CarrierDefaultApp", +} diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk deleted file mode 100644 index 4c6388110ebf..000000000000 --- a/packages/CarrierDefaultApp/tests/unit/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2016, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests -LOCAL_CERTIFICATE := platform - -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base - -LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules mockito-target-minus-junit4 - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := CarrierDefaultAppUnitTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_INSTRUMENTATION_FOR := CarrierDefaultApp - -include $(BUILD_PACKAGE) - diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp new file mode 100644 index 000000000000..a379bfccbe29 --- /dev/null +++ b/packages/CompanionDeviceManager/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_app { + name: "CompanionDeviceManager", + srcs: ["src/**/*.java"], + platform_apis: true, +} diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml new file mode 100644 index 000000000000..824cc6992491 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string> + <string name="chooser_title" msgid="4958797271463138976">"Koppel met <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Koppel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> met <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml new file mode 100644 index 000000000000..5d8950467b7b --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string> + <string name="chooser_title" msgid="4958797271463138976">"ከ<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ጋር አገናኝ"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ከ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> አገናኝ"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml new file mode 100644 index 000000000000..9199986d1348 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string> + <string name="chooser_title" msgid="4958797271463138976">"الربط باستخدام تطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"ربط تطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بجهاز <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml new file mode 100644 index 000000000000..2bd6d7e93d66 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ৰ সৈতে লিংক কৰক"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ৰ সৈতে <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> লিংক কৰক"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml new file mode 100644 index 000000000000..c992cfdc1fcc --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilə əlaqələndirin"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqini <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ilə əlaqələndirin"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..8a388a472579 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string> + <string name="chooser_title" msgid="4958797271463138976">"Povežite sa aplikacijom <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> i uređaj <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml new file mode 100644 index 000000000000..e1b801670267 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string> + <string name="chooser_title" msgid="4958797271463138976">"Звяжыце з праграмай <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Звяжыце праграму <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> з прыладай <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml new file mode 100644 index 000000000000..8748e205d212 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Свързване с(ъс) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Свържете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> с(ъс) <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml new file mode 100644 index 000000000000..a9fb9ff3d9d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-এর সাথে লিঙ্ক করুন"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-এর সাথে <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> লিঙ্ক করুন"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml new file mode 100644 index 000000000000..220553cfcd30 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string> + <string name="chooser_title" msgid="4958797271463138976">"Povežite se s aplikacijom <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> s uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml new file mode 100644 index 000000000000..0ad921a79dd6 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Aplicació Gestor de dispositius complementaris"</string> + <string name="chooser_title" msgid="4958797271463138976">"Enllaça amb <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Enllaça <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> amb <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml new file mode 100644 index 000000000000..ebd9cb192216 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string> + <string name="chooser_title" msgid="4958797271463138976">"Propojení s aplikací <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Propojení aplikace <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> se zařízením <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml new file mode 100644 index 000000000000..7601e40428c1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string> + <string name="chooser_title" msgid="4958797271463138976">"Tilknyt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Knyt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml new file mode 100644 index 000000000000..b58f2c55d1af --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Mit <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> verknüpfen"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mit <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> verknüpfen"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml new file mode 100644 index 000000000000..2d562136849a --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string> + <string name="chooser_title" msgid="4958797271463138976">"Σύνδεση με <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Σύνδεση <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> με <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml new file mode 100644 index 000000000000..91c7643150d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Link with <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Link <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml new file mode 100644 index 000000000000..91c7643150d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Link with <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Link <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml new file mode 100644 index 000000000000..91c7643150d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Link with <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Link <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml new file mode 100644 index 000000000000..91c7643150d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Link with <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Link <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml new file mode 100644 index 000000000000..e052e61ac10b --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Link with <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Link <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml new file mode 100644 index 000000000000..0552ee7ab9f3 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string> + <string name="chooser_title" msgid="4958797271463138976">"Vincular con <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Vincular <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> con <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml new file mode 100644 index 000000000000..c9e218e334f1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string> + <string name="chooser_title" msgid="4958797271463138976">"Vincular con <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Vincular <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> con <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml new file mode 100644 index 000000000000..1ac26f712a18 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string> + <string name="chooser_title" msgid="4958797271463138976">"Linkimine rakendusega <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Rakenduse <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> linkimine seadmega <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml new file mode 100644 index 000000000000..26e19792b4c6 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string> + <string name="chooser_title" msgid="4958797271463138976">"Lotu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioarekin"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Lotu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> gailuarekin"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml new file mode 100644 index 000000000000..b43fe290ad04 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string> + <string name="chooser_title" msgid="4958797271463138976">"پیوند با <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"پیوند <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> با <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml new file mode 100644 index 000000000000..fbc18f60c751 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Linkitä <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Linkitä <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ja <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml new file mode 100644 index 000000000000..05e340299aef --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string> + <string name="chooser_title" msgid="4958797271463138976">"Lier à <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Lier <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml new file mode 100644 index 000000000000..881d6aaea693 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string> + <string name="chooser_title" msgid="4958797271463138976">"Associer à l\'application <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Associer l\'application <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à l\'appareil <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml new file mode 100644 index 000000000000..6f85022a31fb --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string> + <string name="chooser_title" msgid="4958797271463138976">"Vincular con <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Vincular <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> con <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml new file mode 100644 index 000000000000..508850765cbd --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> સાથે લિંક કરો"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> સાથે લિંક કરો"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml new file mode 100644 index 000000000000..4afcb630a8ed --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> से लिंक करें"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> से लिंक करें"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml new file mode 100644 index 000000000000..7b71939476fb --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Povežite s aplikacijom <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Povežite aplikaciju <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> s uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml new file mode 100644 index 000000000000..19920b24fe3f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string> + <string name="chooser_title" msgid="4958797271463138976">"Összekapcsolás a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazással"</string> + <string name="confirmation_title" msgid="5683126664999349196">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás és a(z) <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> eszköz összekapcsolása"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml new file mode 100644 index 000000000000..8dee4a34f3ef --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածի հետ կապում"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածի կապում <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքի հետ"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml new file mode 100644 index 000000000000..efd77fe7b60f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string> + <string name="chooser_title" msgid="4958797271463138976">"Tautkan dengan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Tautkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dengan <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml new file mode 100644 index 000000000000..4bf94474ccb1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string> + <string name="chooser_title" msgid="4958797271463138976">"Tengjast við <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Tengja <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> við <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml new file mode 100644 index 000000000000..a602061f71f1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gestione dispositivi companion"</string> + <string name="chooser_title" msgid="4958797271463138976">"Collega con <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Collega <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> con <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml new file mode 100644 index 000000000000..b95fae5327ee --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string> + <string name="chooser_title" msgid="4958797271463138976">"קישור אל <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"קישור <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> אל <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml new file mode 100644 index 000000000000..8c39e701c9c6 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> とのリンク"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> と <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> とのリンク"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml new file mode 100644 index 000000000000..6fa899a1ac2f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-თან მიბმა"</string> + <string name="confirmation_title" msgid="5683126664999349196">"მიაბით ერთმანეთს <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> და <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml new file mode 100644 index 000000000000..18ab5e6f9d22 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасымен байланыстыру"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасымен және <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысымен байланыстыру"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml new file mode 100644 index 000000000000..42efac4c0674 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"កម្មវិធីគ្រប់គ្រងឧបករណ៍ដៃគូ"</string> + <string name="chooser_title" msgid="4958797271463138976">"ភ្ជាប់ជាមួយ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"ភ្ជាប់ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ជាមួយ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml new file mode 100644 index 000000000000..e21bb02ed9ab --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ನ ಜೊತೆಗೆ ಲಿಂಕ್ ಮಾಡಿ"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಅನ್ನು <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ನ ಜೊತೆಗೆ ಲಿಂಕ್ ಮಾಡಿ"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml new file mode 100644 index 000000000000..17702f58d90a --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 연결"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>을(를) <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>에 연결"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml new file mode 100644 index 000000000000..63efea7ab046 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосу менен байланыштыруу"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосун <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгү менен байланыштыруу"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml new file mode 100644 index 000000000000..f6375515cbe4 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string> + <string name="chooser_title" msgid="4958797271463138976">"ເຊື່ອມຕໍ່ກັບ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"ເຊື່ອມຕໍ່ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ກັບ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml new file mode 100644 index 000000000000..ddaa60146267 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Susieti su pr. <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Susieti programą <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> su <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> įrenginiu"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml new file mode 100644 index 000000000000..ba8840c1645c --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string> + <string name="chooser_title" msgid="4958797271463138976">"Saistīšana ar lietotni <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Lietotnes <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saistīšana ar ierīci <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml new file mode 100644 index 000000000000..4c2e41177658 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Поврзување со <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Поврзување на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> со <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml new file mode 100644 index 000000000000..950e4e4fa346 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ഉപയോഗിച്ച് ലിങ്ക് ചെയ്യുക"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ലിങ്ക് ചെയ്യുക"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml new file mode 100644 index 000000000000..5add54ae1505 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-тай холбох"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-г <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-тай холбох"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml new file mode 100644 index 000000000000..45348c04f6ca --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g><strong> सह लिंक करा"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ला <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> शी लिंक करा"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml new file mode 100644 index 000000000000..5a50f157b132 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string> + <string name="chooser_title" msgid="4958797271463138976">"Paut dengan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Paut <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dengan <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml new file mode 100644 index 000000000000..3fba5ffd9b3b --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<xliff:g id="APP_NAME">%1$s</xliff:g></strong> ဖြင့် ချိတ်ဆက်ရန်<strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ဖြင့် ချိတ်ဆက်ခြင်း <strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml new file mode 100644 index 000000000000..0b49f473e853 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Knytt til <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Knytt <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml new file mode 100644 index 000000000000..348104f844ab --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> सँग लिंक गर्नुहोस्"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> सँग <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई लिंक गर्नुहोस्"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml new file mode 100644 index 000000000000..12177ca92362 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Koppelen met <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> met <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> koppelen"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml new file mode 100644 index 000000000000..b5a9e1c66f29 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ସହ ଲିଙ୍କ୍ କରନ୍ତୁ"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ସହିତ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ଲିଙ୍କ୍ କରନ୍ତୁ"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml new file mode 100644 index 000000000000..70a408f6a1bb --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨਾਲ ਲਿੰਕ ਕਰੋ"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਨਾਲ ਲਿੰਕ ਕਰੋ"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml new file mode 100644 index 000000000000..c622cedc990b --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string> + <string name="chooser_title" msgid="4958797271463138976">"Połącz z: <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Połącz: <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> z: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml new file mode 100644 index 000000000000..b18c3ad260c3 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> + <string name="chooser_title" msgid="4958797271463138976">"Vincular a <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Vincular <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml new file mode 100644 index 000000000000..a64c544736e9 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string> + <string name="chooser_title" msgid="4958797271463138976">"Associe à aplicação <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Associe a aplicação <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ao dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml new file mode 100644 index 000000000000..b18c3ad260c3 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> + <string name="chooser_title" msgid="4958797271463138976">"Vincular a <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Vincular <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml new file mode 100644 index 000000000000..1c7860203cc7 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string> + <string name="chooser_title" msgid="4958797271463138976">"Conectați cu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Conectați <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> cu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml new file mode 100644 index 000000000000..74b91002a2d1 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string> + <string name="chooser_title" msgid="4958797271463138976">"Подключение к приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Подключение приложения <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> к устройству <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml new file mode 100644 index 000000000000..89f4409c0158 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> සමඟ සම්බන්ධ කරන්න"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> සමඟ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> සම්බන්ධ කරන්න"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml new file mode 100644 index 000000000000..21b3b30cab32 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string> + <string name="chooser_title" msgid="4958797271463138976">"Prepojenie s aplikáciou <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Prepojenie <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> so zariadením <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml new file mode 100644 index 000000000000..5645fb13e5db --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string> + <string name="chooser_title" msgid="4958797271463138976">"Povezava z aplikacijo <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Povezava aplikacije <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> z napravo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml new file mode 100644 index 000000000000..793554f8d40d --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string> + <string name="chooser_title" msgid="4958797271463138976">"Lidh me <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Lidh <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> me <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml new file mode 100644 index 000000000000..eac68058cfa2 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string> + <string name="chooser_title" msgid="4958797271463138976">"Повежите са апликацијом <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Повежите апликацију <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> и уређај <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml new file mode 100644 index 000000000000..7d2ad85ba321 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Länka med <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Länka <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> till <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml new file mode 100644 index 000000000000..8308bbd3d78a --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string> + <string name="chooser_title" msgid="4958797271463138976">"Unganisha na <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Ungansha <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml new file mode 100644 index 000000000000..05110374ba30 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸுடன் இணைத்தல்"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> சாதனத்துடன் இணைத்தல்"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml new file mode 100644 index 000000000000..ebcc7f5c10ec --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>తో లింక్ చేయండి"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>తో లింక్ చేయండి"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml new file mode 100644 index 000000000000..3240794631d7 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"ลิงก์กับ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"ลิงก์ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> กับ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml new file mode 100644 index 000000000000..444b0d80c01f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"I-link sa <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"I-link ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> sa <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml new file mode 100644 index 000000000000..9c20ed4faa71 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasına bağlan"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasını <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazına bağla"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml new file mode 100644 index 000000000000..bed8d4d26bf3 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string> + <string name="chooser_title" msgid="4958797271463138976">"Налаштуйте зв’язок із додатком <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Зв’яжіть пристрій <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> з додатком <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml new file mode 100644 index 000000000000..e9ad738d99f7 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string> + <string name="chooser_title" msgid="4958797271463138976">";lt;strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong&gt& سے لنک کریں"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> سے لنک کریں"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml new file mode 100644 index 000000000000..c7d82ad806de --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga ulash"</string> + <string name="confirmation_title" msgid="5683126664999349196">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> with <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ilovasiga ulash"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml new file mode 100644 index 000000000000..29e128c7a40e --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> + <string name="chooser_title" msgid="4958797271463138976">"Liên kết với <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Liên kết <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> với <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml new file mode 100644 index 000000000000..8e962185ab8d --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string> + <string name="chooser_title" msgid="4958797271463138976">"关联 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"将 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 关联到 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml new file mode 100644 index 000000000000..dd055d0b239f --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string> + <string name="chooser_title" msgid="4958797271463138976">"使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>連接"</string> + <string name="confirmation_title" msgid="5683126664999349196">"連結「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>和「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000000..2c46d599bf35 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string> + <string name="chooser_title" msgid="4958797271463138976">"與「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>連結"</string> + <string name="confirmation_title" msgid="5683126664999349196">"為「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>與<strong></strong> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 建立連結"</string> +</resources> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml new file mode 100644 index 000000000000..80a24ff0e225 --- /dev/null +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string> + <string name="chooser_title" msgid="4958797271463138976">"Xhuma ne-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="5683126664999349196">"Xhuma ne-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> nge-<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> +</resources> diff --git a/packages/DefaultContainerService/Android.bp b/packages/DefaultContainerService/Android.bp new file mode 100644 index 000000000000..d4ba6e8980e6 --- /dev/null +++ b/packages/DefaultContainerService/Android.bp @@ -0,0 +1,8 @@ +android_app { + name: "DefaultContainerService", + srcs: ["**/*.java"], + platform_apis: true, + jni_libs: ["libdefcontainer_jni"], + certificate: "platform", + privileged: true, +} diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk deleted file mode 100644 index 10c35c07ccf9..000000000000 --- a/packages/DefaultContainerService/Android.mk +++ /dev/null @@ -1,17 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := DefaultContainerService -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni - -LOCAL_CERTIFICATE := platform - -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) diff --git a/packages/FakeOemFeatures/Android.mk b/packages/DynamicAndroidInstallationService/Android.mk index 43de8e5315cc..13d96ac3906e 100644 --- a/packages/FakeOemFeatures/Android.mk +++ b/packages/DynamicAndroidInstallationService/Android.mk @@ -4,13 +4,16 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res) -LOCAL_PACKAGE_NAME := FakeOemFeatures -LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_USE_AAPT2 := true + +LOCAL_PACKAGE_NAME := DynamicAndroidInstallationService LOCAL_CERTIFICATE := platform +LOCAL_PRIVILEGED_MODULE := true +LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_PROGUARD_ENABLED := disabled -include $(BUILD_PACKAGE) -include $(call all-makefiles-under,$(LOCAL_PATH)) +include $(BUILD_PACKAGE) diff --git a/packages/DynamicAndroidInstallationService/AndroidManifest.xml b/packages/DynamicAndroidInstallationService/AndroidManifest.xml new file mode 100644 index 000000000000..32acad4d5ab0 --- /dev/null +++ b/packages/DynamicAndroidInstallationService/AndroidManifest.xml @@ -0,0 +1,47 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.dynandroid" + android:sharedUserId="android.uid.system"> + + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.MANAGE_DYNAMIC_ANDROID" /> + <uses-permission android:name="android.permission.REBOOT" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + + <application + android:allowBackup="false" + android:label="@string/app_name"> + + <service + android:name=".DynamicAndroidInstallationService" + android:enabled="true" + android:exported="true" + android:permission="android.permission.MANAGE_DYNAMIC_ANDROID" + android:process=":dynandroid"> + <intent-filter> + <action android:name="android.content.action.NOTIFY_IF_IN_USE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </service> + + <activity android:name=".VerificationActivity" + android:exported="true" + android:permission="android.permission.MANAGE_DYNAMIC_ANDROID" + android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar" + android:process=":dynandroid"> + <intent-filter> + <action android:name="android.content.action.START_INSTALL" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <receiver + android:name=".BootCompletedReceiver" + android:enabled="true" + android:exported="false"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> + </application> +</manifest> diff --git a/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2 b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2 diff --git a/packages/DynamicAndroidInstallationService/NOTICE b/packages/DynamicAndroidInstallationService/NOTICE new file mode 100644 index 000000000000..c5b1efa7aac7 --- /dev/null +++ b/packages/DynamicAndroidInstallationService/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml new file mode 100644 index 000000000000..acf1567ab7fe --- /dev/null +++ b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z" + android:fillColor="#4285F4"/> +</vector> diff --git a/packages/DynamicAndroidInstallationService/res/values/strings.xml b/packages/DynamicAndroidInstallationService/res/values/strings.xml new file mode 100644 index 000000000000..221e1d75b133 --- /dev/null +++ b/packages/DynamicAndroidInstallationService/res/values/strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- application name [CHAR LIMIT=32] --> + <string name="app_name">AndroidOnTap Installer</string> + + <!-- notification channel name [CHAR LIMIT=32] --> + <string name="notification_channel_name">AndroidOnTap Installer</string> + + <!-- password page title [CHAR LIMIT=32] --> + <string name="keyguard_title">AndroidOnTap Installer</string> + + <!-- password page description [CHAR LIMIT=128] --> + <string name="keyguard_description">Please enter your password and continue to AndroidOnTap installation</string> + + <!-- Displayed on notification: DynAndroid installation is completed [CHAR LIMIT=128] --> + <string name="notification_install_completed">Installation is completed, you can reboot into the new installed system now.</string> + <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] --> + <string name="notification_install_inprogress">Installation is in progress.</string> + <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] --> + <string name="notification_install_failed">Installation Failed.</string> + <!-- Displayed on notification: We are running in AndroidOnTap [CHAR LIMIT=128] --> + <string name="notification_dynandroid_in_use">We are running in AndroidOnTap.</string> + + <!-- Action on notification: Cancel installation [CHAR LIMIT=16] --> + <string name="notification_action_cancel">Cancel</string> + <!-- Action on notification: Discard installation [CHAR LIMIT=16] --> + <string name="notification_action_discard">Discard</string> + <!-- Action on notification: Uninstall AndroidOnTap [CHAR LIMIT=16] --> + <string name="notification_action_uninstall">Uninstall</string> + <!-- Action on notification: Reboot to AndroidOnTap [CHAR LIMIT=16] --> + <string name="notification_action_reboot_to_dynandroid">Reboot</string> + +</resources> diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java new file mode 100644 index 000000000000..dd1be897b2ea --- /dev/null +++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.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 com.android.dynandroid; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DynamicAndroidClient; +import android.content.Intent; +import android.os.UserHandle; +import android.util.Log; + + +/** + * A BoardcastReceiver waiting for ACTION_BOOT_COMPLETED and ask + * the service to display a notification if we are currently running + * in DynamicAndroid. + */ +public class BootCompletedReceiver extends BroadcastReceiver { + + private static final String TAG = "BootCompletedReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + Log.d(TAG, "Broadcast received: " + action); + + if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + Intent startServiceIntent = new Intent( + context, DynamicAndroidInstallationService.class); + + startServiceIntent.setAction(DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE); + context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM); + } + } +} diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java new file mode 100644 index 000000000000..7755cbc9ca3b --- /dev/null +++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java @@ -0,0 +1,483 @@ +/* + * 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.dynandroid; + +import static android.content.DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE; +import static android.content.DynamicAndroidClient.ACTION_START_INSTALL; +import static android.content.DynamicAndroidClient.CAUSE_ERROR_EXCEPTION; +import static android.content.DynamicAndroidClient.CAUSE_ERROR_INVALID_URL; +import static android.content.DynamicAndroidClient.CAUSE_ERROR_IO; +import static android.content.DynamicAndroidClient.CAUSE_INSTALL_CANCELLED; +import static android.content.DynamicAndroidClient.CAUSE_INSTALL_COMPLETED; +import static android.content.DynamicAndroidClient.CAUSE_NOT_SPECIFIED; +import static android.content.DynamicAndroidClient.STATUS_IN_PROGRESS; +import static android.content.DynamicAndroidClient.STATUS_IN_USE; +import static android.content.DynamicAndroidClient.STATUS_NOT_STARTED; +import static android.content.DynamicAndroidClient.STATUS_READY; +import static android.os.AsyncTask.Status.FINISHED; +import static android.os.AsyncTask.Status.PENDING; +import static android.os.AsyncTask.Status.RUNNING; + +import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_EXCEPTION; +import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_INVALID_URL; +import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_IO; +import static com.android.dynandroid.InstallationAsyncTask.RESULT_OK; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.DynamicAndroidClient; +import android.content.Intent; +import android.os.Bundle; +import android.os.DynamicAndroidManager; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.PowerManager; +import android.os.RemoteException; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * This class is the service in charge of DynamicAndroid installation. + * It also posts status to notification bar and wait for user's + * cancel and confirm commnands. + */ +public class DynamicAndroidInstallationService extends Service + implements InstallationAsyncTask.InstallStatusListener { + + private static final String TAG = "DynAndroidInstallationService"; + + /* + * Intent actions + */ + private static final String ACTION_CANCEL_INSTALL = + "com.android.dynandroid.ACTION_CANCEL_INSTALL"; + private static final String ACTION_REBOOT_TO_DYN_ANDROID = + "com.android.dynandroid.ACTION_REBOOT_TO_DYN_ANDROID"; + private static final String ACTION_REBOOT_TO_NORMAL = + "com.android.dynandroid.ACTION_REBOOT_TO_NORMAL"; + + /* + * For notification + */ + private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynandroid"; + private static final int NOTIFICATION_ID = 1; + + /* + * IPC + */ + /** Keeps track of all current registered clients. */ + ArrayList<Messenger> mClients = new ArrayList<>(); + + /** Handler of incoming messages from clients. */ + final Messenger mMessenger = new Messenger(new IncomingHandler(this)); + + static class IncomingHandler extends Handler { + private final WeakReference<DynamicAndroidInstallationService> mWeakService; + + IncomingHandler(DynamicAndroidInstallationService service) { + mWeakService = new WeakReference<>(service); + } + + @Override + public void handleMessage(Message msg) { + DynamicAndroidInstallationService service = mWeakService.get(); + + if (service != null) { + service.handleMessage(msg); + } + } + } + + private DynamicAndroidManager mDynAndroid; + private NotificationManager mNM; + + private long mSystemSize; + private long mInstalledSize; + private boolean mJustCancelledByUser; + + private PendingIntent mPiCancel; + private PendingIntent mPiRebootToDynamicAndroid; + private PendingIntent mPiUninstallAndReboot; + + private InstallationAsyncTask mInstallTask; + + + @Override + public void onCreate() { + super.onCreate(); + + prepareNotification(); + + mDynAndroid = (DynamicAndroidManager) getSystemService(Context.DYNAMIC_ANDROID_SERVICE); + } + + @Override + public void onDestroy() { + // Cancel the persistent notification. + mNM.cancel(NOTIFICATION_ID); + } + + @Override + public IBinder onBind(Intent intent) { + return mMessenger.getBinder(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + String action = intent.getAction(); + + Log.d(TAG, "onStartCommand(): action=" + action); + + if (ACTION_START_INSTALL.equals(action)) { + executeInstallCommand(intent); + } else if (ACTION_CANCEL_INSTALL.equals(action)) { + executeCancelCommand(); + } else if (ACTION_REBOOT_TO_DYN_ANDROID.equals(action)) { + executeRebootToDynAndroidCommand(); + } else if (ACTION_REBOOT_TO_NORMAL.equals(action)) { + executeRebootToNormalCommand(); + } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) { + executeNotifyIfInUseCommand(); + } + + return Service.START_NOT_STICKY; + } + + @Override + public void onProgressUpdate(long installedSize) { + mInstalledSize = installedSize; + postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED); + } + + @Override + public void onResult(int result) { + if (result == RESULT_OK) { + postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED); + return; + } + + // if it's not successful, reset the task and stop self. + resetTaskAndStop(); + + switch (result) { + case RESULT_ERROR_IO: + postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO); + break; + + case RESULT_ERROR_INVALID_URL: + postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL); + break; + + case RESULT_ERROR_EXCEPTION: + postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION); + break; + } + } + + @Override + public void onCancelled() { + resetTaskAndStop(); + postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED); + } + + private void executeInstallCommand(Intent intent) { + if (!verifyRequest(intent)) { + Log.e(TAG, "Verification failed. Did you use VerificationActivity?"); + return; + } + + if (mInstallTask != null) { + Log.e(TAG, "There is already an install task running"); + return; + } + + if (isInDynamicAndroid()) { + Log.e(TAG, "We are already running in DynamicAndroid"); + return; + } + + String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL); + mSystemSize = intent.getLongExtra(DynamicAndroidClient.KEY_SYSTEM_SIZE, 0); + long userdata = intent.getLongExtra(DynamicAndroidClient.KEY_USERDATA_SIZE, 0); + + mInstallTask = new InstallationAsyncTask(url, mSystemSize, userdata, mDynAndroid, this); + mInstallTask.execute(); + + // start fore ground + startForeground(NOTIFICATION_ID, + buildNotification(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED)); + } + + private void executeCancelCommand() { + if (mInstallTask == null || mInstallTask.getStatus() == PENDING) { + Log.e(TAG, "Cancel command triggered, but there is no task running"); + mNM.cancel(NOTIFICATION_ID); + + return; + } + + mJustCancelledByUser = true; + + if (mInstallTask.cancel(false)) { + // Will cleanup and post status in onCancelled() + Log.d(TAG, "Cancel request filed successfully"); + } else { + Log.d(TAG, "Requested cancel, completed task will be discarded"); + + resetTaskAndStop(); + postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED); + } + + } + + private void executeRebootToDynAndroidCommand() { + if (mInstallTask == null || mInstallTask.getStatus() != FINISHED) { + Log.e(TAG, "Trying to reboot to DynamicAndroid, but there is no complete installation"); + return; + } + + if (!mInstallTask.commit()) { + // TODO: b/123673280 better UI response + Log.e(TAG, "Failed to commit installation because of native runtime error."); + mNM.cancel(NOTIFICATION_ID); + + return; + } + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + + if (powerManager != null) { + powerManager.reboot("dynandroid"); + } + } + + private void executeRebootToNormalCommand() { + mDynAndroid.remove(); + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + + if (powerManager != null) { + powerManager.reboot(null); + } + } + + private void executeNotifyIfInUseCommand() { + if (isInDynamicAndroid()) { + startForeground(NOTIFICATION_ID, + buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED)); + } + } + + private void resetTaskAndStop() { + mInstallTask = null; + + stopForeground(true); + + // stop self, but this service is not destroyed yet if it's still bound + stopSelf(); + } + + private void prepareNotification() { + NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, + getString(R.string.notification_channel_name), + NotificationManager.IMPORTANCE_LOW); + + mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + if (mNM != null) { + mNM.createNotificationChannel(chan); + } + + Intent intentCancel = new Intent(this, DynamicAndroidInstallationService.class); + intentCancel.setAction(ACTION_CANCEL_INSTALL); + mPiCancel = PendingIntent.getService(this, 0, intentCancel, 0); + + Intent intentRebootToDyn = new Intent(this, DynamicAndroidInstallationService.class); + intentRebootToDyn.setAction(ACTION_REBOOT_TO_DYN_ANDROID); + mPiRebootToDynamicAndroid = PendingIntent.getService(this, 0, intentRebootToDyn, 0); + + Intent intentUninstallAndReboot = new Intent(this, DynamicAndroidInstallationService.class); + intentUninstallAndReboot.setAction(ACTION_REBOOT_TO_NORMAL); + mPiUninstallAndReboot = PendingIntent.getService(this, 0, intentUninstallAndReboot, 0); + } + + private Notification buildNotification(int status, int cause) { + Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_system_update_googblue_24dp) + .setProgress(0, 0, false); + + switch (status) { + case STATUS_IN_PROGRESS: + builder.setContentText(getString(R.string.notification_install_inprogress)); + + int max = (int) Math.max(mSystemSize >> 20, 1); + int progress = (int) mInstalledSize >> 20; + + builder.setProgress(max, progress, false); + + builder.addAction(new Notification.Action.Builder( + null, getString(R.string.notification_action_cancel), + mPiCancel).build()); + + break; + + case STATUS_READY: + builder.setContentText(getString(R.string.notification_install_completed)); + + builder.addAction(new Notification.Action.Builder( + null, getString(R.string.notification_action_reboot_to_dynandroid), + mPiRebootToDynamicAndroid).build()); + + builder.addAction(new Notification.Action.Builder( + null, getString(R.string.notification_action_cancel), + mPiCancel).build()); + + break; + + case STATUS_IN_USE: + builder.setContentText(getString(R.string.notification_dynandroid_in_use)); + + builder.addAction(new Notification.Action.Builder( + null, getString(R.string.notification_action_uninstall), + mPiUninstallAndReboot).build()); + + break; + + case STATUS_NOT_STARTED: + if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) { + builder.setContentText(getString(R.string.notification_install_failed)); + } else { + // no need to notify the user if the task is not started, or cancelled. + } + break; + + default: + throw new IllegalStateException("status is invalid"); + } + + return builder.build(); + } + + private boolean verifyRequest(Intent intent) { + String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL); + + return VerificationActivity.isVerified(url); + } + + private void postStatus(int status, int cause) { + Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause); + + boolean notifyOnNotificationBar = true; + + if (status == STATUS_NOT_STARTED + && cause == CAUSE_INSTALL_CANCELLED + && mJustCancelledByUser) { + // if task is cancelled by user, do not notify them + notifyOnNotificationBar = false; + mJustCancelledByUser = false; + } + + if (notifyOnNotificationBar) { + mNM.notify(NOTIFICATION_ID, buildNotification(status, cause)); + } + + for (int i = mClients.size() - 1; i >= 0; i--) { + try { + notifyOneClient(mClients.get(i), status, cause); + } catch (RemoteException e) { + mClients.remove(i); + } + } + } + + private void notifyOneClient(Messenger client, int status, int cause) throws RemoteException { + Bundle bundle = new Bundle(); + + bundle.putLong(DynamicAndroidClient.KEY_INSTALLED_SIZE, mInstalledSize); + + client.send(Message.obtain(null, + DynamicAndroidClient.MSG_POST_STATUS, status, cause, bundle)); + } + + private int getStatus() { + if (isInDynamicAndroid()) { + return STATUS_IN_USE; + + } else if (mInstallTask == null) { + return STATUS_NOT_STARTED; + + } + + switch (mInstallTask.getStatus()) { + case PENDING: + return STATUS_NOT_STARTED; + + case RUNNING: + return STATUS_IN_PROGRESS; + + case FINISHED: + int result = mInstallTask.getResult(); + + if (result == RESULT_OK) { + return STATUS_READY; + } else { + throw new IllegalStateException("A failed InstallationTask is not reset"); + } + + default: + return STATUS_NOT_STARTED; + } + } + + private boolean isInDynamicAndroid() { + return mDynAndroid.isInUse(); + } + + void handleMessage(Message msg) { + switch (msg.what) { + case DynamicAndroidClient.MSG_REGISTER_LISTENER: + try { + Messenger client = msg.replyTo; + + int status = getStatus(); + + // tell just registered client my status, but do not specify cause + notifyOneClient(client, status, CAUSE_NOT_SPECIFIED); + + mClients.add(client); + } catch (RemoteException e) { + // do nothing if we cannot send update to the client + e.printStackTrace(); + } + + break; + case DynamicAndroidClient.MSG_UNREGISTER_LISTENER: + mClients.remove(msg.replyTo); + break; + default: + // do nothing + } + } +} diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java new file mode 100644 index 000000000000..3c759e948b4e --- /dev/null +++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java @@ -0,0 +1,205 @@ +/* + * 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.dynandroid; + +import android.os.AsyncTask; +import android.os.DynamicAndroidManager; +import android.util.Log; +import android.webkit.URLUtil; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Locale; +import java.util.zip.GZIPInputStream; + + +class InstallationAsyncTask extends AsyncTask<String, Long, Integer> { + + private static final String TAG = "InstallationAsyncTask"; + + private static final int READ_BUFFER_SIZE = 1 << 19; + + private class InvalidImageUrlException extends RuntimeException { + private InvalidImageUrlException(String message) { + super(message); + } + } + + + /** Not completed, including being cancelled */ + static final int NO_RESULT = 0; + static final int RESULT_OK = 1; + static final int RESULT_ERROR_IO = 2; + static final int RESULT_ERROR_INVALID_URL = 3; + static final int RESULT_ERROR_EXCEPTION = 6; + + interface InstallStatusListener { + void onProgressUpdate(long installedSize); + void onResult(int resultCode); + void onCancelled(); + } + + private final String mUrl; + private final long mSystemSize; + private final long mUserdataSize; + private final DynamicAndroidManager mDynamicAndroid; + private final InstallStatusListener mListener; + private DynamicAndroidManager.Session mInstallationSession; + + private long mInstalledSize; + private long mReportedInstalledSize; + private int mResult = NO_RESULT; + + private InputStream mStream; + + + InstallationAsyncTask(String url, long systemSize, long userdataSize, + DynamicAndroidManager dynAndroid, InstallStatusListener listener) { + mUrl = url; + mSystemSize = systemSize; + mUserdataSize = userdataSize; + mDynamicAndroid = dynAndroid; + mListener = listener; + } + + @Override + protected void onPreExecute() { + mListener.onProgressUpdate(0); + } + + @Override + protected Integer doInBackground(String... voids) { + Log.d(TAG, "Start doInBackground(), URL: " + mUrl); + + try { + // call start in background + mInstallationSession = mDynamicAndroid.startInstallation(mSystemSize, mUserdataSize); + + if (mInstallationSession == null) { + Log.e(TAG, "Failed to start installation with requested size: " + + (mSystemSize + mUserdataSize)); + + return RESULT_ERROR_IO; + } + + initInputStream(); + + byte[] bytes = new byte[READ_BUFFER_SIZE]; + + int numBytesRead; + long minStepToReport = mSystemSize / 100; + + Log.d(TAG, "Start installation loop"); + while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) { + if (isCancelled()) { + break; + } + + byte[] writeBuffer = numBytesRead == READ_BUFFER_SIZE + ? bytes : Arrays.copyOf(bytes, numBytesRead); + + if (!mInstallationSession.write(writeBuffer)) { + throw new IOException("Failed write() to DynamicAndroid"); + } + + mInstalledSize += numBytesRead; + + if (mInstalledSize > mReportedInstalledSize + minStepToReport) { + publishProgress(mInstalledSize); + mReportedInstalledSize = mInstalledSize; + } + } + + return RESULT_OK; + + } catch (IOException e) { + e.printStackTrace(); + return RESULT_ERROR_IO; + + } catch (InvalidImageUrlException e) { + e.printStackTrace(); + return RESULT_ERROR_INVALID_URL; + + } catch (Exception e) { + e.printStackTrace(); + return RESULT_ERROR_EXCEPTION; + + } finally { + close(); + } + } + + @Override + protected void onCancelled() { + Log.d(TAG, "onCancelled(), URL: " + mUrl); + + close(); + + mListener.onCancelled(); + } + + @Override + protected void onPostExecute(Integer result) { + Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result); + + close(); + + mResult = result; + mListener.onResult(mResult); + } + + @Override + protected void onProgressUpdate(Long... values) { + long progress = values[0]; + mListener.onProgressUpdate(progress); + } + + private void initInputStream() throws IOException, InvalidImageUrlException { + if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) { + mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream())); + } else { + throw new InvalidImageUrlException( + String.format(Locale.US, "Unsupported file source: %s", mUrl)); + } + } + + private void close() { + try { + if (mStream != null) { + mStream.close(); + mStream = null; + } + } catch (IOException e) { + // ignore + } + } + + int getResult() { + return mResult; + } + + boolean commit() { + if (mInstallationSession == null) { + return false; + } + + return mInstallationSession.commit(); + } +} diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java new file mode 100644 index 000000000000..c18c4fe689c9 --- /dev/null +++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java @@ -0,0 +1,104 @@ +/* + * 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.dynandroid; + +import static android.content.DynamicAndroidClient.KEY_SYSTEM_SIZE; +import static android.content.DynamicAndroidClient.KEY_SYSTEM_URL; +import static android.content.DynamicAndroidClient.KEY_USERDATA_SIZE; + +import android.app.Activity; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.DynamicAndroidClient; +import android.content.Intent; +import android.os.Bundle; +import android.os.UserHandle; +import android.util.Log; + + +/** + * This Activity starts KeyguardManager and ask the user to confirm + * before any installation request. If the device is not protected by + * a password, it approves the request by default. + */ +public class VerificationActivity extends Activity { + + private static final String TAG = "VerificationActivity"; + + private static final int REQUEST_CODE = 1; + + // For install request verification + private static String sVerifiedUrl; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); + + if (km != null) { + String title = getString(R.string.keyguard_title); + String description = getString(R.string.keyguard_description); + Intent intent = km.createConfirmDeviceCredentialIntent(title, description); + + if (intent == null) { + Log.d(TAG, "This device is not protected by a password/pin"); + startInstallationService(); + finish(); + } else { + startActivityForResult(intent, REQUEST_CODE); + } + } else { + finish(); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { + startInstallationService(); + } + + finish(); + } + + private void startInstallationService() { + // retrieve data from calling intent + Intent callingIntent = getIntent(); + + String url = callingIntent.getStringExtra(KEY_SYSTEM_URL); + long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0); + long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0); + + sVerifiedUrl = url; + + // start service + Intent intent = new Intent(this, DynamicAndroidInstallationService.class); + intent.setAction(DynamicAndroidClient.ACTION_START_INSTALL); + intent.putExtra(KEY_SYSTEM_URL, url); + intent.putExtra(KEY_SYSTEM_SIZE, systemSize); + intent.putExtra(KEY_USERDATA_SIZE, userdataSize); + + Log.d(TAG, "Starting Installation Service"); + startServiceAsUser(intent, UserHandle.SYSTEM); + } + + static boolean isVerified(String url) { + return sVerifiedUrl != null && sVerifiedUrl.equals(url); + } +} 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/FakeOemFeatures/Android.bp b/packages/FakeOemFeatures/Android.bp new file mode 100644 index 000000000000..b265158220da --- /dev/null +++ b/packages/FakeOemFeatures/Android.bp @@ -0,0 +1,9 @@ +android_app { + name: "FakeOemFeatures", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", + optimize: { + enabled: false, + }, +} diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp new file mode 100644 index 000000000000..e794f726dba6 --- /dev/null +++ b/packages/FusedLocation/Android.bp @@ -0,0 +1,22 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_app { + name: "FusedLocation", + srcs: ["**/*.java"], + libs: ["com.android.location.provider"], + platform_apis: true, + certificate: "platform", + privileged: true, +} diff --git a/packages/FusedLocation/Android.mk b/packages/FusedLocation/Android.mk deleted file mode 100644 index d795870251db..000000000000 --- a/packages/FusedLocation/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2012 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_JAVA_LIBRARIES := com.android.location.provider - -LOCAL_PACKAGE_NAME := FusedLocation -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) diff --git a/packages/LocalTransport/Android.bp b/packages/LocalTransport/Android.bp new file mode 100644 index 000000000000..2c990fed1688 --- /dev/null +++ b/packages/LocalTransport/Android.bp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "LocalTransport", + srcs: ["src/**/*.java"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + platform_apis: true, + certificate: "platform", + privileged: true, +} diff --git a/packages/LocalTransport/Android.mk b/packages/LocalTransport/Android.mk deleted file mode 100644 index 3484b0f7a537..000000000000 --- a/packages/LocalTransport/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_PACKAGE_NAME := LocalTransport -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -######################## -include $(call all-makefiles-under,$(LOCAL_PATH)) - 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..4b846b0fb372 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); @@ -1339,26 +1318,28 @@ public class NetworkMonitor extends StateMachine { // is needed (i.e. can't browse a 204). This could be the result of an HTTP // proxy server. if (httpResponseCode == 200) { + long contentLength = urlConnection.getContentLengthLong(); if (probeType == ValidationProbeEvent.PROBE_PAC) { validationLog( probeType, url, "PAC fetch 200 response interpreted as 204 response."); httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE; - } else if (urlConnection.getContentLengthLong() == 0) { - // Consider 200 response with "Content-length=0" to not be a captive portal. - // There's no point in considering this a captive portal as the user cannot - // sign-in to an empty page. Probably the result of a broken transparent proxy. - // See http://b/9972012. - validationLog(probeType, url, - "200 response with Content-length=0 interpreted as 204 response."); - httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE; - } else if (urlConnection.getContentLengthLong() == -1) { - // When no Content-length (default value == -1), attempt to read a byte from the - // response. Do not use available() as it is unreliable. See http://b/33498325. + } else if (contentLength == -1) { + // When no Content-length (default value == -1), attempt to read a byte + // from the response. Do not use available() as it is unreliable. + // See http://b/33498325. if (urlConnection.getInputStream().read() == -1) { - validationLog( - probeType, url, "Empty 200 response interpreted as 204 response."); - httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE; + validationLog(probeType, url, + "Empty 200 response interpreted as failed response."); + httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; } + } else if (contentLength <= 4) { + // Consider 200 response with "Content-length <= 4" to not be a captive + // portal. There's no point in considering this a captive portal as the + // user cannot sign-in to an empty page. Probably the result of a broken + // transparent proxy. See http://b/9972012 and http://b/122999481. + validationLog(probeType, url, "200 response with Content-length <= 4" + + " interpreted as failed response."); + httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; } } } catch (IOException e) { @@ -1485,10 +1466,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 +1473,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 +1496,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..3414397d73c3 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; @@ -39,9 +40,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.SocketKeepalive; -import android.net.TcpKeepalivePacketData; -import android.net.TcpKeepalivePacketData.TcpSocketInfo; +import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; @@ -1017,6 +1016,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}; @@ -1544,12 +1544,15 @@ public class ApfTest { InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); - final TcpSocketInfo v4Tsi = new TcpSocketInfo( - srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); - final TcpKeepalivePacketData ipv4TcpKeepalivePacket = - TcpKeepalivePacketData.tcpKeepalivePacket(v4Tsi); + final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); + parcel.srcAddress = srcAddr.getAddress(); + parcel.srcPort = srcPort; + parcel.dstAddress = dstAddr.getAddress(); + parcel.dstPort = dstPort; + parcel.seq = seqNum; + parcel.ack = ackNum; - apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); + apfFilter.addKeepalivePacketFilter(slot1, parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped @@ -1568,7 +1571,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); @@ -1578,11 +1581,17 @@ public class ApfTest { // dst: 2404:0:0:0:0:0:faf2, port: 54321 srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR); dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR); - final TcpSocketInfo v6Tsi = new TcpSocketInfo( - srcAddr, srcPort, dstAddr, dstPort, seqNum, ackNum, window, windowScale); - final TcpKeepalivePacketData ipv6TcpKeepalivePacket = - TcpKeepalivePacketData.tcpKeepalivePacket(v6Tsi); - apfFilter.addKeepalivePacketFilter(slot1, ipv6TcpKeepalivePacket.toStableParcelable()); + + final TcpKeepalivePacketDataParcelable ipv6Parcel = + new TcpKeepalivePacketDataParcelable(); + ipv6Parcel.srcAddress = srcAddr.getAddress(); + ipv6Parcel.srcPort = srcPort; + ipv6Parcel.dstAddress = dstAddr.getAddress(); + ipv6Parcel.dstPort = dstPort; + ipv6Parcel.seq = seqNum; + ipv6Parcel.ack = ackNum; + + apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv6 keepalive ack packet is dropped @@ -1604,8 +1613,8 @@ public class ApfTest { apfFilter.removeKeepalivePacketFilter(slot1); // Verify multiple filters - apfFilter.addKeepalivePacketFilter(slot1, ipv4TcpKeepalivePacket.toStableParcelable()); - apfFilter.addKeepalivePacketFilter(slot2, ipv6TcpKeepalivePacket.toStableParcelable()); + apfFilter.addKeepalivePacketFilter(slot1, parcel); + apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped @@ -1613,15 +1622,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 @@ -1641,7 +1650,7 @@ public class ApfTest { // Remove keepalive filters apfFilter.removeKeepalivePacketFilter(slot1); apfFilter.removeKeepalivePacketFilter(slot2); - } catch (SocketKeepalive.InvalidPacketException e) { + } catch (UnsupportedOperationException e) { // TODO: support V6 packets } @@ -1650,13 +1659,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 +1673,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/OsuLogin/Android.bp b/packages/OsuLogin/Android.bp new file mode 100644 index 000000000000..ac3abaca7a39 --- /dev/null +++ b/packages/OsuLogin/Android.bp @@ -0,0 +1,8 @@ +android_app { + name: "OsuLogin", + static_libs: ["androidx.legacy_legacy-support-v4"], + resource_dirs: ["res"], + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", +} diff --git a/packages/OsuLogin/Android.mk b/packages/OsuLogin/Android.mk deleted file mode 100644 index 2c076159ec12..000000000000 --- a/packages/OsuLogin/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_USE_AAPT2 := true -LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4 -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := OsuLogin -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml index 6d35550f1b77..b198f5a35630 100644 --- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml +++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml @@ -52,7 +52,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textAppearance="@android:style/TextAppearance.Material.Subhead" + android:textAppearance="?android:attr/textAppearanceListItem" android:ellipsize="marquee" android:fadingEdge="horizontal"/> @@ -65,7 +65,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAppearance="?android:attr/textAppearanceSmall" android:textAlignment="viewStart" android:textColor="?android:attr/textColorSecondary"/> @@ -73,7 +73,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textAppearance="@android:style/TextAppearance.Material.Small" + android:textAppearance="?android:attr/textAppearanceSmall" android:textAlignment="viewEnd" android:textColor="?android:attr/textColorSecondary" android:maxLines="1" diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java index d332bac3bb6d..3b87fcabb3fc 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java @@ -87,6 +87,7 @@ public class BarChartPreference extends Preference { }; private int mMaxBarHeight; + private boolean mIsLoading; private BarChartInfo mBarChartInfo; public BarChartPreference(Context context) { @@ -134,12 +135,33 @@ public class BarChartPreference extends Preference { notifyChanged(); } + /** + * Set loading state for {@link BarChartPreference}. + * + * By default, {@link BarChartPreference} doesn't care about it. + * + * But if user sets loading state to true explicitly, it means {@link BarChartPreference} + * needs to take some time to load data. So we won't initialize any view now. + * + * Once the state is updated to false, we will start to initialize view again. + * + * @param isLoading whether or not {@link BarChartPreference} is in loading state. + */ + public void updateLoadingState(boolean isLoading) { + mIsLoading = isLoading; + notifyChanged(); + } + @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); holder.setDividerAllowedAbove(true); holder.setDividerAllowedBelow(true); + // If the state is loading, we just show a blank view. + if (mIsLoading) { + return; + } // We must show title of bar chart. bindChartTitleView(holder); 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/common.mk b/packages/SettingsLib/common.mk index 8d24eaba5cc4..8c309ff97370 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -31,4 +31,3 @@ LOCAL_STATIC_ANDROID_LIBRARIES += \ androidx.legacy_legacy-preference-v14 \ SettingsLib -LOCAL_RESOURCE_DIR += $(call my-dir)/res 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/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java index c3ea336e0a4e..1080cf49e551 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.view.View; import android.widget.TextView; @@ -46,6 +47,7 @@ public class BarChartPreferenceTest { private BarView mBarView2; private BarView mBarView3; private BarView mBarView4; + private TextView mTitleView; private TextView mDetailsView; private PreferenceViewHolder mHolder; private BarChartPreference mPreference; @@ -63,6 +65,7 @@ public class BarChartPreferenceTest { mBarView2 = mBarChartView.findViewById(R.id.bar_view2); mBarView3 = mBarChartView.findViewById(R.id.bar_view3); mBarView4 = mBarChartView.findViewById(R.id.bar_view4); + mTitleView = mBarChartView.findViewById(R.id.bar_chart_title); mDetailsView = mBarChartView.findViewById(R.id.bar_chart_details); mBarChartInfo = new BarChartInfo.Builder() @@ -76,7 +79,6 @@ public class BarChartPreferenceTest { @Test public void initializeBarChart_titleSet_shouldSetTitleInChartView() { - final TextView titleView = mBarChartView.findViewById(R.id.bar_chart_title); final BarChartInfo barChartInfo = new BarChartInfo.Builder() .setTitle(R.string.debug_app) .build(); @@ -84,8 +86,8 @@ public class BarChartPreferenceTest { mPreference.initializeBarChart(barChartInfo); mPreference.onBindViewHolder(mHolder); - assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(titleView.getText()).isEqualTo(mContext.getText(R.string.debug_app)); + assertThat(mTitleView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mTitleView.getText()).isEqualTo(mContext.getText(R.string.debug_app)); } @Test @@ -99,8 +101,7 @@ public class BarChartPreferenceTest { // We don't add any bar view yet. mPreference.onBindViewHolder(mHolder); - assertThat(mBarChartView.findViewById(R.id.bar_chart_title).getVisibility()) - .isEqualTo(View.VISIBLE); + assertThat(mTitleView.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mBarChartView.findViewById(R.id.empty_view).getVisibility()) .isEqualTo(View.VISIBLE); assertThat(mBarChartView.findViewById(R.id.bar_views_container).getVisibility()) @@ -302,4 +303,38 @@ public class BarChartPreferenceTest { assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mBarView1.hasOnClickListeners()).isTrue(); } + + @Test + public void onBindViewHolder_loadingStateIsTrue_shouldNotInitAnyView() { + final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app); + viewInfo.setClickListener(v -> { + }); + final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo}; + + mPreference.initializeBarChart(mBarChartInfo); + mPreference.setBarViewInfos(barViewsInfo); + mPreference.updateLoadingState(true /* isLoading */); + + mPreference.onBindViewHolder(mHolder); + + assertThat(TextUtils.isEmpty(mTitleView.getText())).isTrue(); + assertThat(TextUtils.isEmpty(mDetailsView.getText())).isTrue(); + } + + @Test + public void onBindViewHolder_loadingStateIsFalse_shouldInitAnyView() { + final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app); + viewInfo.setClickListener(v -> { + }); + final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo}; + + mPreference.initializeBarChart(mBarChartInfo); + mPreference.setBarViewInfos(barViewsInfo); + mPreference.updateLoadingState(false /* isLoading */); + + mPreference.onBindViewHolder(mHolder); + + assertThat(TextUtils.isEmpty(mTitleView.getText())).isFalse(); + assertThat(TextUtils.isEmpty(mDetailsView.getText())).isFalse(); + } } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp new file mode 100644 index 000000000000..1c97fc37cf50 --- /dev/null +++ b/packages/SettingsProvider/Android.bp @@ -0,0 +1,40 @@ +android_app { + name: "SettingsProvider", + resource_dirs: ["res"], + srcs: [ + "src/**/*.java", + "src/com/android/providers/settings/EventLogTags.logtags", + ], + libs: [ + "telephony-common", + "ims-common", + ], + static_libs: ["junit"], + platform_apis: true, + certificate: "platform", + privileged: true, +} + +android_test { + name: "SettingsProviderTest", + // Note we statically link several classes to do some unit tests. It's not accessible otherwise + // because this test is not an instrumentation test. (because the target runs in the system process.) + srcs: [ + "test/**/*.java", + "src/com/android/providers/settings/SettingsState.java", + "src/com/android/providers/settings/SettingsHelper.java", + ], + static_libs: ["androidx.test.rules"], + libs: ["android.test.base"], + resource_dirs: ["res"], + aaptflags: [ + "--auto-add-overlay", + "--extra-packages", + "com.android.providers.settings", + ], + platform_apis: true, + certificate: "platform", + test_suites: ["device-tests"], + manifest: "test/AndroidManifest.xml", + test_config: "test/AndroidTest.xml", +} diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk deleted file mode 100644 index ccde5716ef93..000000000000 --- a/packages/SettingsProvider/Android.mk +++ /dev/null @@ -1,22 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - src/com/android/providers/settings/EventLogTags.logtags - -LOCAL_JAVA_LIBRARIES := telephony-common ims-common -LOCAL_STATIC_JAVA_LIBRARIES := junit - -LOCAL_PACKAGE_NAME := SettingsProvider -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -######################## -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index db4b131a0f6f..81b304dc2ce4 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -724,6 +724,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.GAME_DRIVER_BLACKLISTS, GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLISTS); + dumpSetting(s, p, + Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, + GlobalSettingsProto.Gpu.GAME_DRIVER_SPHAL_LIBRARIES); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk deleted file mode 100644 index ac97adb2c5c5..000000000000 --- a/packages/SettingsProvider/test/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -# Note we statically link several classes to do some unit tests. It's not accessible otherwise -# because this test is not an instrumentation test. (because the target runs in the system process.) -LOCAL_SRC_FILES := $(call all-subdir-java-files) \ - ../src/com/android/providers/settings/SettingsState.java \ - ../src/com/android/providers/settings/SettingsHelper.java - -LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules - -LOCAL_JAVA_LIBRARIES := android.test.base - -LOCAL_RESOURCE_DIR := frameworks/base/packages/SettingsProvider/res - -LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.providers.settings - -LOCAL_PACKAGE_NAME := SettingsProviderTest -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_MODULE_TAGS := tests - -LOCAL_CERTIFICATE := platform - -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp new file mode 100644 index 000000000000..5380832a17d4 --- /dev/null +++ b/packages/SharedStorageBackup/Android.bp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "SharedStorageBackup", + srcs: ["src/**/*.java"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + platform_apis: true, + certificate: "platform", + privileged: true, +} diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk deleted file mode 100644 index 2e07ab18d71a..000000000000 --- a/packages/SharedStorageBackup/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_PACKAGE_NAME := SharedStorageBackup -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -######################## -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp new file mode 100644 index 000000000000..586292efa20b --- /dev/null +++ b/packages/StatementService/Android.bp @@ -0,0 +1,27 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +android_app { + name: "StatementService", + srcs: ["src/**/*.java"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + platform_apis: true, + privileged: true, + libs: ["org.apache.http.legacy"], + static_libs: [ + "libprotobuf-java-nano", + "volley", + ], +} diff --git a/packages/StatementService/Android.mk b/packages/StatementService/Android.mk deleted file mode 100644 index b9b29e752193..000000000000 --- a/packages/StatementService/Android.mk +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_PACKAGE_NAME := StatementService -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_JAVA_LIBRARIES += org.apache.http.legacy - -LOCAL_STATIC_JAVA_LIBRARIES := \ - libprotobuf-java-nano \ - volley - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)/src) diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index e0d178fb9a1e..29ae1b29792b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -137,6 +137,7 @@ android_app { static_libs: [ "SystemUI-core", ], + resource_dirs: [], platform_apis: true, product_specific: true, 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-keyguard/drawable-xxxhdpi/bubble_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png Binary files differdeleted file mode 100644 index 67f072f54795..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png Binary files differdeleted file mode 100644 index 63927bc4deaf..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png Binary files differdeleted file mode 100644 index a538149ca3d8..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index 51f6a4b92413..dc45b4b51925 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -28,11 +28,20 @@ android:clipToPadding="false" android:orientation="vertical" android:layout_centerHorizontal="true"> + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="64dp" + android:paddingEnd="64dp" + android:visibility="gone" + android:textColor="?attr/wallpaperTextColor" + android:theme="@style/TextAppearance.Keyguard" + /> <view class="com.android.keyguard.KeyguardSliceView$Row" android:id="@+id/row" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/subtitle_clock_padding" android:orientation="horizontal" android:gravity="center" /> diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml index b61ad9c4fc11..9f7ea0d44357 100644 --- a/packages/SystemUI/res-keyguard/layout/text_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml @@ -16,9 +16,10 @@ --> <TextClock xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:gravity="center_horizontal" android:letterSpacing="0.03" android:textColor="?attr/wallpaperTextColor" android:singleLine="true" diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index e2ba23ec898c..b6a41c19ec32 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -42,18 +42,18 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Slice header --> - <dimen name="widget_title_font_size">24dp</dimen> - <dimen name="widget_title_bottom_margin">14dp</dimen> - <dimen name="bottom_text_spacing_digital">0dp</dimen> + <dimen name="widget_title_font_size">22dp</dimen> + <dimen name="header_subtitle_padding">4dp</dimen> + <dimen name="header_icon_size">20dp</dimen> <!-- Slice subtitle --> <dimen name="widget_label_font_size">16dp</dimen> <!-- Clock without header --> <dimen name="widget_big_font_size">64dp</dimen> + <dimen name="bottom_text_spacing_digital">0dp</dimen> <!-- Clock with header --> - <dimen name="widget_small_clock_padding">-25dp</dimen> - <dimen name="widget_small_font_size">24dp</dimen> - <dimen name="widget_small_font_stroke">0.6dp</dimen> + <dimen name="widget_small_font_size">22dp</dimen> <dimen name="widget_vertical_padding">32dp</dimen> + <dimen name="widget_vertical_padding_clock">30dp</dimen> <!-- Subtitle paddings --> <dimen name="widget_horizontal_padding">8dp</dimen> <dimen name="widget_icon_size">16dp</dimen> 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..480f5235f75a --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml @@ -0,0 +1,81 @@ +<?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_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_width="wrap_content" + 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="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:layoutDirection="ltr" + android:background="?android:attr/colorBackgroundFloating" + android:gravity="center" + 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..4f868263226d --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml @@ -0,0 +1,83 @@ +<?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="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:layoutDirection="rtl" + android:background="?android:attr/colorBackgroundFloating" + android:gravity="center" + 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_marginBottom="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + > + <LinearLayout + android:layout_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..729e96ebc22e 100644 --- a/packages/SystemUI/res/layout/global_actions_grid.xml +++ b/packages/SystemUI/res/layout/global_actions_grid.xml @@ -12,69 +12,61 @@ > <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--> <LinearLayout android:id="@+id/separated_button" android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:layout_marginLeft="@dimen/global_actions_grid_side_margin" android:layout_marginRight="@dimen/global_actions_grid_side_margin" - android:paddingTop="@dimen/global_actions_grid_top_padding" - android:paddingLeft="@dimen/global_actions_grid_left_padding" - android:paddingBottom="@dimen/global_actions_grid_bottom_padding" - android:paddingRight="@dimen/global_actions_grid_right_padding" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" android:orientation="vertical" android:background="?android:attr/colorBackgroundFloating" + android:gravity="center" 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" android:translationZ="@dimen/global_actions_translate" - android:paddingLeft="@dimen/global_actions_grid_left_padding" - android:paddingRight="@dimen/global_actions_grid_right_padding" - android:paddingTop="@dimen/global_actions_grid_top_padding" - android:paddingBottom="@dimen/global_actions_grid_bottom_padding" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" android:background="?android:attr/colorBackgroundFloating" > <LinearLayout android:layout_width="wrap_content" 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..5dee09dac947 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml @@ -18,42 +18,41 @@ work around this for now with LinearLayouts. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="72dp" - android:layout_height="72dp" - android:gravity="center" + android:layout_width="@dimen/global_actions_grid_item_width" + android:layout_height="@dimen/global_actions_grid_item_height" + android:gravity="top|center_horizontal" android:orientation="vertical" android:layout_marginTop="@dimen/global_actions_grid_item_vertical_margin" android:layout_marginBottom="@dimen/global_actions_grid_item_vertical_margin" android:layout_marginLeft="@dimen/global_actions_grid_item_side_margin" android:layout_marginRight="@dimen/global_actions_grid_item_side_margin" - android:paddingEnd="4dip" - android:paddingStart="4dip"> - +> <ImageView android:id="@*android:id/icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_gravity="center" + android:layout_width="@dimen/global_actions_grid_item_icon_width" + android:layout_height="@dimen/global_actions_grid_item_icon_height" + android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin" + android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin" + android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin" android:scaleType="center" android:alpha="?android:attr/primaryContentAlpha" /> <TextView android:id="@*android:id/message" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:paddingTop="10dp" android:gravity="center" - android:textSize="12sp" + android:textSize="12dp" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView + android:visibility="gone" android:id="@*android:id/status" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" android:gravity="center" android:textColor="?android:attr/textColorTertiary" android:textAppearance="?android:attr/textAppearanceSmall" diff --git a/packages/SystemUI/res/layout/home_handle.xml b/packages/SystemUI/res/layout/home_handle.xml new file mode 100644 index 000000000000..48ea5c47bc7c --- /dev/null +++ b/packages/SystemUI/res/layout/home_handle.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.systemui.statusbar.phone.NavigationHandle + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/home_handle" + android:layout_width="@dimen/navigation_handle_width" + android:layout_height="match_parent" + android:layout_weight="0" + /> + 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-sw320dp-land/dimens.xml b/packages/SystemUI/res/values-sw320dp-land/dimens.xml new file mode 100644 index 000000000000..2ec5abd673cb --- /dev/null +++ b/packages/SystemUI/res/values-sw320dp-land/dimens.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 + --> +<resources> + + <!-- Global actions grid --> + <dimen name="global_actions_grid_vertical_padding">3dp</dimen> + <dimen name="global_actions_grid_horizontal_padding">0dp</dimen> + + <dimen name="global_actions_grid_item_side_margin">4dp</dimen> + <dimen name="global_actions_grid_item_vertical_margin">5dp</dimen> + +</resources> + diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml new file mode 100644 index 000000000000..0c2b1cc5e679 --- /dev/null +++ b/packages/SystemUI/res/values-sw320dp/dimens.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<resources> + + <!-- Global actions grid --> + <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen> + + <dimen name="global_actions_grid_vertical_padding">0dp</dimen> + <dimen name="global_actions_grid_horizontal_padding">3dp</dimen> + + <dimen name="global_actions_grid_item_side_margin">5dp</dimen> + <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen> + <dimen name="global_actions_grid_item_width">64dp</dimen> + <dimen name="global_actions_grid_item_height">64dp</dimen> + + <dimen name="global_actions_grid_item_icon_width">18dp</dimen> + <dimen name="global_actions_grid_item_icon_height">18dp</dimen> + <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen> + <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen> + <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> + +</resources> + diff --git a/packages/SystemUI/res/values-sw410dp-land/dimens.xml b/packages/SystemUI/res/values-sw410dp-land/dimens.xml new file mode 100644 index 000000000000..61ba2d02da9e --- /dev/null +++ b/packages/SystemUI/res/values-sw410dp-land/dimens.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 + --> +<resources> + + <!-- Global actions grid --> + <dimen name="global_actions_grid_vertical_padding">4dp</dimen> + <dimen name="global_actions_grid_horizontal_padding">8dp</dimen> + + <dimen name="global_actions_grid_item_side_margin">8dp</dimen> + <dimen name="global_actions_grid_item_vertical_margin">12dp</dimen> + +</resources> + diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml index 5ce65241c9ca..4197eb2519fa 100644 --- a/packages/SystemUI/res/values-sw410dp/dimens.xml +++ b/packages/SystemUI/res/values-sw410dp/dimens.xml @@ -21,4 +21,22 @@ for different hardware and product builds. --> <resources> <dimen name="qs_detail_items_padding_top">16dp</dimen> + + <!-- Global actions grid --> + <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen> + + <dimen name="global_actions_grid_vertical_padding">8dp</dimen> + <dimen name="global_actions_grid_horizontal_padding">4dp</dimen> + + <dimen name="global_actions_grid_item_side_margin">12dp</dimen> + <dimen name="global_actions_grid_item_vertical_margin">8dp</dimen> + <dimen name="global_actions_grid_item_width">72dp</dimen> + <dimen name="global_actions_grid_item_height">72dp</dimen> + + <dimen name="global_actions_grid_item_icon_width">24dp</dimen> + <dimen name="global_actions_grid_item_icon_height">24dp</dimen> + <dimen name="global_actions_grid_item_icon_top_margin">16dp</dimen> + <dimen name="global_actions_grid_item_icon_side_margin">24dp</dimen> + <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> + </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 76eb85eb4695..4396a42929ba 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -325,6 +325,7 @@ <!-- Nav bar button default ordering/layout --> <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string> <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string> + <string name="config_navBarLayoutHandle" translatable="false">";home_handle;"</string> <bool name="quick_settings_show_full_alarm">false</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1e1245fe0d86..371a0604c45b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -33,6 +33,11 @@ <!-- size of the dead zone when touches have recently occurred elsewhere on screen --> <dimen name="navigation_bar_deadzone_size_max">32dp</dimen> + <!-- dimensions for the navigation bar handle --> + <dimen name="navigation_handle_width">180dp</dimen> + <dimen name="navigation_handle_radius">2dp</dimen> + <dimen name="navigation_handle_bottom">8dp</dimen> + <!-- Height of notification icons in the status bar --> <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> @@ -841,26 +846,15 @@ <dimen name="default_gear_space">18dp</dimen> <dimen name="cell_overlay_padding">18dp</dimen> + <!-- Global actions power menu --> <dimen name="global_actions_panel_width">120dp</dimen> - - <dimen name="global_actions_grid_container_bottom_margin">16dp</dimen> - - <dimen name="global_actions_grid_side_margin">4dp</dimen> - <dimen name="global_actions_grid_separated_panel_width">104dp</dimen> - <dimen name="global_actions_grid_top_padding">8dp</dimen> - <dimen name="global_actions_grid_bottom_padding">8dp</dimen> - <dimen name="global_actions_grid_left_padding">4dp</dimen> - <dimen name="global_actions_grid_right_padding">4dp</dimen> - - <dimen name="global_actions_grid_item_side_margin">12dp</dimen> - <dimen name="global_actions_grid_item_vertical_margin">8dp</dimen> - <dimen name="global_actions_top_padding">120dp</dimen> - <dimen name="global_actions_padding">12dp</dimen> - <dimen name="global_actions_translate">9dp</dimen> + <!-- Global actions grid layout --> + <dimen name="global_actions_grid_side_margin">4dp</dimen> + <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> <dimen name="burn_in_prevention_offset_x">8dp</dimen> @@ -980,26 +974,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> @@ -1013,8 +1013,8 @@ <dimen name="bubble_icon_inset">16dp</dimen> <!-- Padding around the view displayed when the bubble is expanded --> <dimen name="bubble_expanded_view_padding">8dp</dimen> - <!-- Default height of the expanded view shown when the bubble is expanded --> - <dimen name="bubble_expanded_default_height">400dp</dimen> + <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded --> + <dimen name="bubble_expanded_default_height">180dp</dimen> <!-- Height of the triangle that points to the expanded bubble --> <dimen name="bubble_pointer_height">4dp</dimen> <!-- Width of the triangle that points to the expanded bubble --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7d009b5a9f18..0fde2de1e2f1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1069,7 +1069,10 @@ <string name="battery_saver_notification_action_text">Turn off Battery Saver</string> <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] --> - <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string> + <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything on your screen including notifications, passwords, photos, messages and payment information.</string> + + <!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] --> + <string name="media_projection_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> to record or cast your screen?</string> <!-- Media projection permission dialog permanent grant check box. [CHAR LIMIT=NONE] --> <string name="media_projection_remember_text">Don\'t show again</string> @@ -1582,19 +1585,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] --> @@ -2323,7 +2326,7 @@ <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]--> <string name="ongoing_privacy_dialog_ok">Got it</string> - <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]--> + <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]--> <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string> <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 822920e63460..8de84bf4e2af 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,10 +1,19 @@ package com.android.keyguard; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Paint; import android.graphics.Paint.Style; +import android.transition.ChangeBounds; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.transition.TransitionValues; import android.util.AttributeSet; +import android.util.MathUtils; +import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -16,6 +25,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; import com.android.systemui.Dependency; +import com.android.systemui.Interpolators; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -28,6 +38,7 @@ import java.util.TimeZone; */ public class KeyguardClockSwitch extends RelativeLayout { + private final Transition mTransition; /** * Optional/alternative clock injected via plugin. */ @@ -53,6 +64,10 @@ public class KeyguardClockSwitch extends RelativeLayout { * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + /** + * If the Keyguard Slice has a header (big center-aligned text.) + */ + private boolean mShowingHeader; private boolean mSupportsDarkText; private int[] mColorPalette; @@ -98,6 +113,7 @@ public class KeyguardClockSwitch extends RelativeLayout { public KeyguardClockSwitch(Context context, AttributeSet attrs) { super(context, attrs); + mTransition = new ClockBoundsTransition(); } /** @@ -286,6 +302,26 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + /** + * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock + * in these cases. + */ + public void setKeyguardShowingHeader(boolean hasHeader) { + if (mShowingHeader == hasHeader || hasCustomClock()) { + return; + } + mShowingHeader = hasHeader; + + TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition); + int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader + ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size); + int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader + ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding); + mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); + mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(), + mClockView.getPaddingRight(), paddingBottom); + } + @VisibleForTesting (otherwise = VisibleForTesting.NONE) ClockManager.ClockChangedListener getClockChangedListener() { return mClockChangedListener; @@ -295,4 +331,54 @@ public class KeyguardClockSwitch extends RelativeLayout { StatusBarStateController.StateListener getStateListener() { return mStateListener; } + + /** + * Special layout transition that scales the clock view as its bounds change, to make it look + * like the text is shrinking. + */ + private class ClockBoundsTransition extends ChangeBounds { + + ClockBoundsTransition() { + setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2); + setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + } + + @Override + public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, + TransitionValues endValues) { + Animator animator = super.createAnimator(sceneRoot, startValues, endValues); + if (animator == null || startValues.view != mClockView) { + return animator; + } + + ValueAnimator boundsAnimator = null; + if (animator instanceof AnimatorSet) { + Animator first = ((AnimatorSet) animator).getChildAnimations().get(0); + if (first instanceof ValueAnimator) { + boundsAnimator = (ValueAnimator) first; + } + } else if (animator instanceof ValueAnimator) { + boundsAnimator = (ValueAnimator) animator; + } + + if (boundsAnimator != null) { + float bigFontSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.widget_big_font_size); + float smallFontSize = mContext.getResources() + .getDimensionPixelSize(R.dimen.widget_small_font_size); + float startScale = mShowingHeader + ? bigFontSize / smallFontSize : smallFontSize / bigFontSize; + boundsAnimator.addUpdateListener(animation -> { + float scale = MathUtils.lerp(startScale, 1f /* stop */, + animation.getAnimatedFraction()); + mClockView.setPivotX(mClockView.getWidth() / 2); + mClockView.setPivotY(0); + mClockView.setScaleX(scale); + mClockView.setScaleY(scale); + }); + } + + return animator; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index bac7844c024b..2040a76b61d3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -37,10 +37,10 @@ import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.Button; import android.widget.LinearLayout; +import android.widget.TextView; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; @@ -58,6 +58,7 @@ import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; +import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -78,6 +79,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private final HashMap<View, PendingIntent> mClickActions; private Uri mKeyguardSliceUri; + @VisibleForTesting + TextView mTitle; private Row mRow; private int mTextColor; private float mDarkAmount = 0; @@ -91,6 +94,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private Runnable mContentChangeListener; private Slice mSlice; private boolean mHasHeader; + private final int mRowWithHeaderPadding; + private final int mRowPadding; public KeyguardSliceView(Context context) { this(context, null, 0); @@ -107,6 +112,9 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI); mClickActions = new HashMap<>(); + mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding); + mRowWithHeaderPadding = context.getResources() + .getDimensionPixelSize(R.dimen.header_subtitle_padding); LayoutTransition transition = new LayoutTransition(); transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); @@ -117,13 +125,13 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN); transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); transition.setAnimateParentHierarchy(false); - transition.addTransitionListener(new SliceViewTransitionListener()); setLayoutTransition(transition); } @Override protected void onFinishInflate() { super.onFinishInflate(); + mTitle = findViewById(R.id.title); mRow = findViewById(R.id.row); mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size); @@ -160,6 +168,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private void showSlice() { Trace.beginSection("KeyguardSliceView#showSlice"); if (mSlice == null) { + mTitle.setVisibility(GONE); mRow.setVisibility(GONE); mHasHeader = false; if (mContentChangeListener != null) { @@ -170,8 +179,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe ListContent lc = new ListContent(getContext(), mSlice); SliceContent headerContent = lc.getHeader(); - mHasHeader = headerContent != null - && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); + mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM); List<SliceContent> subItems = new ArrayList<>(); for (int i = 0; i < lc.getRowItems().size(); i++) { SliceContent subItem = lc.getRowItems().get(i); @@ -181,12 +189,26 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe subItems.add(subItem); } } + if (!mHasHeader) { + mTitle.setVisibility(GONE); + } else { + mTitle.setVisibility(VISIBLE); + + RowContent header = lc.getHeader(); + SliceItem mainTitle = header.getTitleItem(); + CharSequence title = mainTitle != null ? mainTitle.getText() : null; + mTitle.setText(title); + } mClickActions.clear(); final int subItemsCount = subItems.size(); final int blendedColor = getTextColor(); final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); + LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); + layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; + mRow.setLayoutParams(layoutParams); + for (int i = startIndex; i < subItemsCount; i++) { RowContent rc = (RowContent) subItems.get(i); SliceItem item = rc.getSliceItem(); @@ -250,6 +272,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private void updateTextColors() { final int blendedColor = getTextColor(); + mTitle.setTextColor(blendedColor); int childCount = mRow.getChildCount(); for (int i = 0; i < childCount; i++) { View v = mRow.getChildAt(i); @@ -294,7 +317,10 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe setupUri(newValue); } - private void setupUri(String uriString) { + /** + * Sets the slice provider Uri. + */ + public void setupUri(String uriString) { if (uriString == null) { uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI; } @@ -512,29 +538,4 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } } } - - private class SliceViewTransitionListener implements LayoutTransition.TransitionListener { - @Override - public void startTransition(LayoutTransition transition, ViewGroup container, View view, - int transitionType) { - switch (transitionType) { - case LayoutTransition.APPEARING: - int translation = getResources().getDimensionPixelSize( - R.dimen.pulsing_notification_appear_translation); - view.setTranslationY(translation); - view.animate() - .translationY(0) - .setDuration(DEFAULT_ANIM_DURATION) - .setInterpolator(Interpolators.ALPHA_IN) - .start(); - break; - } - } - - @Override - public void endTransition(LayoutTransition transition, ViewGroup container, View view, - int transitionType) { - - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index bb549ada6830..b0670fd0a91a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -16,14 +16,11 @@ package com.android.keyguard; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; -import android.graphics.Paint; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -37,15 +34,12 @@ import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.GridLayout; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.graphics.ColorUtils; import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; import com.android.systemui.statusbar.policy.ConfigurationController; import com.google.android.collect.Sets; @@ -54,14 +48,13 @@ import java.util.Locale; import java.util.TimeZone; public class KeyguardStatusView extends GridLayout implements - ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener { + ConfigurationController.ConfigurationListener { private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardStatusView"; private static final int MARQUEE_DELAY_MS = 2000; private final LockPatternUtils mLockPatternUtils; private final IActivityManager mIActivityManager; - private final float mSmallClockScale; private TextView mLogoutView; private KeyguardClockSwitch mClockView; @@ -74,8 +67,6 @@ public class KeyguardStatusView extends GridLayout implements private boolean mPulsing; private float mDarkAmount = 0; private int mTextColor; - private int mLastLayoutHeight; - private int mSmallClockPadding; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -135,8 +126,6 @@ public class KeyguardStatusView extends GridLayout implements mIActivityManager = ActivityManager.getService(); mLockPatternUtils = new LockPatternUtils(getContext()); mHandler = new Handler(Looper.myLooper()); - mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size) - / getResources().getDimension(R.dimen.widget_big_font_size); onDensityOrFontScaleChanged(); } @@ -189,9 +178,6 @@ public class KeyguardStatusView extends GridLayout implements mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice); mTextColor = mClockView.getCurrentTextColor(); - int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke); - mClockView.getPaint().setStrokeWidth(clockStroke); - mClockView.addOnLayoutChangeListener(this); mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged); onSliceContentChanged(); @@ -207,72 +193,20 @@ public class KeyguardStatusView extends GridLayout implements * Moves clock, adjusting margins when slice content changes. */ private void onSliceContentChanged() { - LinearLayout.LayoutParams layoutParams = - (LinearLayout.LayoutParams) mClockView.getLayoutParams(); - layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0; - mClockView.setLayoutParams(layoutParams); - } - - /** - * Animate clock when necessary. - */ - @Override - public void onLayoutChange(View view, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - boolean smallClock = mKeyguardSlice.hasHeader(); - int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight; - long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION; - long delay = smallClock ? 0 : duration / 4; - - boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null - && mKeyguardSlice.getLayoutTransition().isRunning(); - if (view == mClockView) { - float clockScale = smallClock ? mSmallClockScale : 1; - Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL; - mClockView.animate().cancel(); - if (shouldAnimate) { - mClockView.setY(oldTop + heightOffset); - mClockView.animate() - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setDuration(duration) - .setListener(new ClipChildrenAnimationListener()) - .setStartDelay(delay) - .y(top) - .scaleX(clockScale) - .scaleY(clockScale) - .withEndAction(() -> { - mClockView.setStyle(style); - mClockView.invalidate(); - }) - .start(); - } else { - mClockView.setY(top); - mClockView.setScaleX(clockScale); - mClockView.setScaleY(clockScale); - mClockView.setStyle(style); - mClockView.invalidate(); - } - } + mClockView.setKeyguardShowingHeader(mKeyguardSlice.hasHeader()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mClockView.setPivotX(mClockView.getWidth() / 2); - mClockView.setPivotY(0); - mLastLayoutHeight = getHeight(); layoutOwnerInfo(); } @Override public void onDensityOrFontScaleChanged() { - mSmallClockPadding = getResources() - .getDimensionPixelSize(R.dimen.widget_small_clock_padding); if (mClockView != null) { mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen.widget_big_font_size)); - mClockView.getPaint().setStrokeWidth( - getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke)); } if (mOwnerInfo != null) { mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, @@ -461,24 +395,4 @@ public class KeyguardStatusView extends GridLayout implements Log.e(TAG, "Failed to logout user", re); } } - - private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements - ViewClippingUtil.ClippingParameters { - - ClipChildrenAnimationListener() { - ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */, - this /* clippingParams */); - } - - @Override - public void onAnimationEnd(Animator animation) { - ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */, - this /* clippingParams */); - } - - @Override - public boolean shouldFinish(View view) { - return view == getParent(); - } - } } 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/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 95981427b642..c5dc3244d0a3 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -15,41 +15,60 @@ */ package com.android.keyguard.clock; +import android.annotation.Nullable; +import android.app.WallpaperManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; import android.os.Handler; import android.os.Looper; import android.provider.Settings; +import android.util.ArrayMap; +import android.util.DisplayMetrics; import android.view.LayoutInflater; +import android.view.View; +import android.view.View.MeasureSpec; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.R; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.ExtensionController.Extension; +import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; +import java.util.Map; import java.util.function.Supplier; import javax.inject.Inject; import javax.inject.Singleton; /** - * Manages custom clock faces. + * Manages custom clock faces for AOD and lock screen. */ @Singleton public final class ClockManager { - private final LayoutInflater mLayoutInflater; - private final ContentResolver mContentResolver; - private final List<ClockInfo> mClockInfos = new ArrayList<>(); /** + * Map from expected value stored in settings to supplier of custom clock face. + */ + private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>(); + @Nullable private ClockPlugin mCurrentClock; + + private final ContentResolver mContentResolver; + private final SettingsWrapper mSettingsWrapper; + /** * Observe settings changes to know when to switch the clock face. */ private final ContentObserver mContentObserver = @@ -57,29 +76,48 @@ public final class ClockManager { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - if (mClockExtension != null) { - mClockExtension.reload(); - } + reload(); } }; - - private final ExtensionController mExtensionController; /** - * Used to select between plugin or default implementations of ClockPlugin interface. + * Observe changes to dock state to know when to switch the clock face. */ - private Extension<ClockPlugin> mClockExtension; + private final DockEventListener mDockEventListener = + new DockEventListener() { + @Override + public void onEvent(int event) { + mIsDocked = (event == DockManager.STATE_DOCKED + || event == DockManager.STATE_DOCKED_HIDE); + reload(); + } + }; + @Nullable private final DockManager mDockManager; /** - * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads. + * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face + * to show. */ - private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin; + private boolean mIsDocked; private final List<ClockChangedListener> mListeners = new ArrayList<>(); + private final SysuiColorExtractor mColorExtractor; + private final int mWidth; + private final int mHeight; + @Inject - public ClockManager(Context context, ExtensionController extensionController) { - mExtensionController = extensionController; - mLayoutInflater = LayoutInflater.from(context); - mContentResolver = context.getContentResolver(); + public ClockManager(Context context, InjectionInflationController injectionInflater, + @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor) { + this(context, injectionInflater, dockManager, colorExtractor, context.getContentResolver(), + new SettingsWrapper(context.getContentResolver())); + } + + ClockManager(Context context, InjectionInflationController injectionInflater, + @Nullable DockManager dockManager, SysuiColorExtractor colorExtractor, + ContentResolver contentResolver, SettingsWrapper settingsWrapper) { + mDockManager = dockManager; + mColorExtractor = colorExtractor; + mContentResolver = contentResolver; + mSettingsWrapper = settingsWrapper; Resources res = context.getResources(); mClockInfos.add(ClockInfo.builder() @@ -94,22 +132,35 @@ public final class ClockManager { .setTitle(res.getString(R.string.clock_title_bubble)) .setId(BubbleClockController.class.getName()) .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail)) - .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_preview)) + .setPreview(() -> getClockPreview(BubbleClockController.class.getName())) .build()); mClockInfos.add(ClockInfo.builder() .setName("stretch") .setTitle(res.getString(R.string.clock_title_stretch)) .setId(StretchAnalogClockController.class.getName()) .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail)) - .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_preview)) + .setPreview(() -> getClockPreview(StretchAnalogClockController.class.getName())) .build()); mClockInfos.add(ClockInfo.builder() .setName("type") .setTitle(res.getString(R.string.clock_title_type)) .setId(TypeClockController.class.getName()) .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail)) - .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview)) + .setPreview(() -> getClockPreview(TypeClockController.class.getName())) .build()); + + LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); + mClocks.put(BubbleClockController.class.getName(), + () -> BubbleClockController.build(layoutInflater)); + mClocks.put(StretchAnalogClockController.class.getName(), + () -> StretchAnalogClockController.build(layoutInflater)); + mClocks.put(TypeClockController.class.getName(), + () -> TypeClockController.build(layoutInflater)); + + // Store the size of the display for generation of clock preview. + DisplayMetrics dm = res.getDisplayMetrics(); + mWidth = dm.widthPixels; + mHeight = dm.heightPixels; } /** @@ -120,9 +171,7 @@ public final class ClockManager { register(); } mListeners.add(listener); - if (mClockExtension != null) { - mClockExtension.reload(); - } + reload(); } /** @@ -142,7 +191,66 @@ public final class ClockManager { return mClockInfos; } - private void setClockPlugin(ClockPlugin plugin) { + /** + * Get the current clock. + * @returns current custom clock or null for default. + */ + @Nullable + ClockPlugin getCurrentClock() { + return mCurrentClock; + } + + @VisibleForTesting + boolean isDocked() { + return mIsDocked; + } + + @VisibleForTesting + ContentObserver getContentObserver() { + return mContentObserver; + } + + /** + * Generate a realistic preview of a clock face. + * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}. + * Returns null if clockId is not found. + */ + @Nullable + private Bitmap getClockPreview(String clockId) { + Supplier<ClockPlugin> supplier = mClocks.get(clockId); + if (supplier == null) { + return null; + } + ClockPlugin plugin = supplier.get(); + + // Use the big clock view for the preview + View clockView = plugin.getBigClockView(); + if (clockView == null) { + return null; + } + + // Initialize state of plugin before generating preview. + plugin.setDarkAmount(1f); + plugin.setTextColor(Color.WHITE); + + ColorExtractor.GradientColors colors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, + true); + plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); + plugin.dozeTimeTick(); + + // Draw clock view hierarchy to canvas. + Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY)); + clockView.layout(0, 0, mWidth, mHeight); + canvas.drawColor(Color.BLACK); + clockView.draw(canvas); + + return bitmap; + } + + private void notifyClockChanged(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple // listeners. This should be fine for now since there is only a single listener. @@ -154,39 +262,48 @@ public final class ClockManager { mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), false, mContentObserver); - mClockExtension = mExtensionController.newExtension(ClockPlugin.class) - .withPlugin(ClockPlugin.class) - .withCallback(mClockPluginConsumer) - // Using withDefault even though this isn't the default as a workaround. - // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin - // instance based off of the value of a setting. Since multiple "default" - // can be provided, using a supplier that changes the settings value. - // A null return will cause Extension#reload to look at the next "default" - // supplier. - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - BubbleClockController.class.getName(), - () -> BubbleClockController.build(mLayoutInflater))) - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - StretchAnalogClockController.class.getName(), - () -> StretchAnalogClockController.build(mLayoutInflater))) - .withDefault( - new SettingsGattedSupplier( - mContentResolver, - Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, - TypeClockController.class.getName(), - () -> TypeClockController.build(mLayoutInflater))) - .build(); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), + false, mContentObserver); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + } } private void unregister() { mContentResolver.unregisterContentObserver(mContentObserver); - mClockExtension.destroy(); + if (mDockManager != null) { + mDockManager.removeListener(mDockEventListener); + } + } + + private void reload() { + mCurrentClock = getClockPlugin(); + notifyClockChanged(mCurrentClock); + } + + private ClockPlugin getClockPlugin() { + ClockPlugin plugin = null; + if (mIsDocked) { + final String name = mSettingsWrapper.getDockedClockFace(); + if (name != null) { + Supplier<ClockPlugin> supplier = mClocks.get(name); + if (supplier != null) { + plugin = supplier.get(); + if (plugin != null) { + return plugin; + } + } + } + } + final String name = mSettingsWrapper.getLockScreenCustomClockFace(); + if (name != null) { + Supplier<ClockPlugin> supplier = mClocks.get(name); + if (supplier != null) { + plugin = supplier.get(); + } + } + return plugin; } /** @@ -200,44 +317,4 @@ public final class ClockManager { */ void onClockChanged(ClockPlugin clock); } - - /** - * Supplier that only gets an instance when a settings value matches expected value. - */ - private static class SettingsGattedSupplier implements Supplier<ClockPlugin> { - - private final ContentResolver mContentResolver; - private final String mKey; - private final String mValue; - private final Supplier<ClockPlugin> mSupplier; - - /** - * Constructs a supplier that changes secure setting key against value. - * - * @param contentResolver Used to look up settings value. - * @param key Settings key. - * @param value If the setting matches this values that get supplies a ClockPlugin - * instance. - * @param supplier Supplier of ClockPlugin instance, only used if the setting - * matches value. - */ - SettingsGattedSupplier(ContentResolver contentResolver, String key, String value, - Supplier<ClockPlugin> supplier) { - mContentResolver = contentResolver; - mKey = key; - mValue = value; - mSupplier = supplier; - } - - /** - * Returns null if the settings value doesn't match the expected value. - * - * A null return causes Extension#reload to skip this supplier and move to the next. - */ - @Override - public ClockPlugin get() { - final String currentValue = Settings.Secure.getString(mContentResolver, mKey); - return Objects.equals(currentValue, mValue) ? mSupplier.get() : null; - } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java index e35cf113c111..22753805c95e 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java @@ -37,7 +37,7 @@ public class ImageClock extends FrameLayout { private ImageView mHourHand; private ImageView mMinuteHand; - private Calendar mTime; + private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); private String mDescFormat; private TimeZone mTimeZone; @@ -51,7 +51,6 @@ public class ImageClock extends FrameLayout { public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mTime = Calendar.getInstance(); mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); } @@ -98,7 +97,7 @@ public class ImageClock extends FrameLayout { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); onTimeChanged(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java new file mode 100644 index 000000000000..58e11553af9d --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java @@ -0,0 +1,48 @@ +/* + * 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.keyguard.clock; + +import android.content.ContentResolver; +import android.provider.Settings; + +/** + * Wrapper around Settings used for testing. + */ +public class SettingsWrapper { + + private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; + private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; + + private ContentResolver mContentResolver; + + public SettingsWrapper(ContentResolver contentResolver) { + mContentResolver = contentResolver; + } + + /** + * Gets the value stored in settings for the custom clock face. + */ + public String getLockScreenCustomClockFace() { + return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE); + } + + /** + * Gets the value stored in settings for the clock face to use when docked. + */ + public String getDockedClockFace() { + return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java index 3c9a4f821c62..34c855bc33d3 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java @@ -37,7 +37,7 @@ public class StretchAnalogClock extends View { private final Paint mHourPaint = new Paint(); private final Paint mMinutePaint = new Paint(); - private Calendar mTime; + private Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); private TimeZone mTimeZone; public StretchAnalogClock(Context context) { @@ -138,7 +138,7 @@ public class StretchAnalogClock extends View { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); onTimeChanged(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java index 6f1b59c69865..7bce3c5cb63f 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java @@ -44,7 +44,7 @@ public class TypographicClock extends TextView { private final String[] mHours; private final String[] mMinutes; private int mAccentColor; - private Calendar mTime; + private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); private String mDescFormat; private TimeZone mTimeZone; @@ -58,7 +58,6 @@ public class TypographicClock extends TextView { public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mTime = Calendar.getInstance(); mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); mResources = context.getResources(); mHours = mResources.getStringArray(R.array.type_clock_hours); @@ -111,12 +110,13 @@ public class TypographicClock extends TextView { */ public void setClockColor(int color) { mAccentColor = color; + onTimeChanged(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); + mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); onTimeChanged(); } } 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..8c49d56ae348 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,11 @@ public abstract class MultiListLayout extends LinearLayout { } return null; } + + /** + * Interface to provide callbacks which trigger when this list detects a rotation. + */ + public 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 755d6fcffb43..6bb4fb5ef94a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -31,6 +31,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -40,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; @@ -141,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, @@ -153,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) { @@ -228,6 +220,16 @@ public class SystemUIFactory { return SysUiServiceProvider.getComponent(context, StatusBar.class); } + /** + * Provides DockManager. + */ + @Singleton + @Provides + @Nullable + public DockManager providesDockManager(Context context) { + return SysUiServiceProvider.getComponent(context, DockManager.class); + } + @Module protected static class ContextHolder { private Context mContext; 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/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java new file mode 100644 index 000000000000..4fe09a92a1de --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -0,0 +1,55 @@ +/* + * 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.view.LayoutInflater; + +import com.android.systemui.R; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +/** + * Encapsulates the data and UI elements of a bubble. + */ +class Bubble { + + public BubbleView iconView; + public BubbleExpandedView expandedView; + public String key; + public NotificationEntry entry; + + Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView, + BubbleExpandedView.OnBubbleBlockedListener listener) { + entry = e; + key = entry.key; + + iconView = (BubbleView) inflater.inflate( + R.layout.bubble_view, stackView, false /* attachToRoot */); + iconView.setNotif(entry); + + expandedView = (BubbleExpandedView) inflater.inflate( + R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); + expandedView.setEntry(entry, stackView); + + expandedView.setOnBlockedListener(listener); + } + + public void setEntry(NotificationEntry entry) { + key = entry.key; + iconView.update(entry); + expandedView.update(entry); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 41bc1b29075c..0832296b7dab 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -16,10 +16,6 @@ package com.android.systemui.bubbles; -import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; -import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -33,21 +29,14 @@ import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.INotificationManager; import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; -import android.content.pm.ActivityInfo; -import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; -import android.util.Log; -import android.util.StatsLog; import android.view.Display; -import android.view.LayoutInflater; import android.view.ViewGroup; -import android.view.WindowManager; import android.widget.FrameLayout; import androidx.annotation.MainThread; @@ -63,13 +52,10 @@ 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.HashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -105,11 +91,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe private final BubbleTaskStackListener mTaskStackListener; private BubbleStateChangeListener mStateChangeListener; private BubbleExpandListener mExpandListener; - private LayoutInflater mInflater; - private final Map<String, BubbleView> mBubbles = new HashMap<>(); + private BubbleData mBubbleData; private BubbleStackView mStackView; - private final Point mDisplaySize; // Bubbles get added to the status bar view private final StatusBarWindowController mStatusBarWindowController; @@ -166,12 +150,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } @Inject - public BubbleController(Context context, StatusBarWindowController statusBarWindowController) { + public BubbleController(Context context, StatusBarWindowController statusBarWindowController, + BubbleData data) { mContext = context; - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mDisplaySize = new Point(); - wm.getDefaultDisplay().getSize(mDisplaySize); - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mNotificationEntryManager.addNotificationEntryListener(mEntryListener); @@ -190,6 +171,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mActivityTaskManager = ActivityTaskManager.getService(); mTaskStackListener = new BubbleTaskStackListener(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + + mBubbleData = data; } /** @@ -219,8 +202,11 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * screen (e.g. if on AOD). */ public boolean hasBubbles() { - for (BubbleView bv : mBubbles.values()) { - if (!bv.getEntry().isBubbleDismissed()) { + if (mStackView == null) { + return false; + } + for (Bubble bubble : mBubbleData.getBubbles()) { + if (!bubble.entry.isBubbleDismissed()) { return true; } } @@ -250,10 +236,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mStackView == null) { return; } - Set<String> keys = mBubbles.keySet(); - for (String key: keys) { - mBubbles.get(key).getEntry().setBubbleDismissed(true); - } mStackView.stackDismissed(); updateVisibility(); @@ -261,19 +243,28 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } /** + * Directs a back gesture at the bubble stack. When opened, the current expanded bubble + * is forwarded a back key down/up pair. + */ + public void performBackPressIfNeeded() { + if (mStackView != null) { + mStackView.performBackPressIfNeeded(); + } + } + + /** * Adds or updates a bubble associated with the provided notification entry. * * @param notif the notification associated with this bubble. * @param updatePosition whether this update should promote the bubble to the top of the stack. */ public void updateBubble(NotificationEntry notif, boolean updatePosition) { - if (mBubbles.containsKey(notif.key)) { + if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { // It's an update - BubbleView bubble = mBubbles.get(notif.key); - mStackView.updateBubble(bubble, notif, updatePosition); + 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 @@ -286,15 +277,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mStackView.setOnBlockedListener(this); } // It's new - BubbleView bubble = (BubbleView) mInflater.inflate( - R.layout.bubble_view, mStackView, false /* attachToRoot */); - bubble.setNotif(notif); - PendingIntent bubbleIntent = getValidBubbleIntent(notif); - if (bubbleIntent != null) { - bubble.setBubbleIntent(bubbleIntent); - } - mBubbles.put(bubble.getKey(), bubble); - mStackView.addBubble(bubble); + mStackView.addBubble(notif); } updateVisibility(); } @@ -306,25 +289,18 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe */ @MainThread void removeBubble(String key) { - BubbleView bv = mBubbles.remove(key); - if (mStackView != null && bv != null) { - mStackView.removeBubble(bv); - bv.destroyActivityView(mStackView); - } - - NotificationEntry entry = bv != null ? bv.getEntry() : null; - if (entry != null) { - entry.setBubbleDismissed(true); - mNotificationEntryManager.updateNotifications(); + if (mStackView != null) { + mStackView.removeBubble(key); } + mNotificationEntryManager.updateNotifications(); updateVisibility(); } @Override public void onBubbleBlocked(NotificationEntry entry) { - Object[] bubbles = mBubbles.values().toArray(); + Object[] bubbles = mBubbleData.getBubbles().toArray(); for (int i = 0; i < bubbles.length; i++) { - NotificationEntry e = ((BubbleView) bubbles[i]).getEntry(); + NotificationEntry e = ((Bubble) bubbles[i]).entry; boolean samePackage = entry.notification.getPackageName().equals( e.notification.getPackageName()); if (samePackage) { @@ -349,8 +325,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; } @@ -368,10 +343,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe && alertAgain(entry, entry.notification.getNotification())) { entry.setShowInShadeWhenBubble(true); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed - if (mBubbles.containsKey(entry.key)) { - mBubbles.get(entry.key).updateDotVisibility(); - } updateBubble(entry, true /* updatePosition */); + mStackView.updateDotVisibility(entry.key); } } @@ -383,8 +356,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return; } entry.setShowInShadeWhenBubble(false); - if (mBubbles.containsKey(entry.key)) { - mBubbles.get(entry.key).updateDotVisibility(); + if (mStackView != null) { + mStackView.updateDotVisibility(entry.key); } if (!removedByUser) { // This was a cancel so we should remove the bubble @@ -441,65 +414,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return mStackView; } - @Nullable - private PendingIntent getValidBubbleIntent(NotificationEntry notif) { - Notification notification = notif.notification.getNotification(); - String packageName = notif.notification.getPackageName(); - Notification.BubbleMetadata data = notif.getBubbleMetadata(); - if (data != null && canLaunchInActivityView(data.getIntent(), - true /* enable logging for bubbles */, packageName)) { - return data.getIntent(); - } - if (shouldUseContentIntent(mContext) - && canLaunchInActivityView(notification.contentIntent, - false /* disable logging for notifications */, packageName)) { - Log.d(TAG, "[addBubble " + notif.key - + "]: No appOverlayIntent, using contentIntent."); - return notification.contentIntent; - } - Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView."); - return null; - } - - /** - * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. - * - * @param intent the pending intent of the bubble. - * @param enableLogging whether bubble developer error should be logged. - * @param packageName the notification package name for this bubble. - * @return - */ - private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging, - String packageName) { - if (intent == null) { - return false; - } - ActivityInfo info = - intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0); - if (info == null) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); - } - return false; - } - if (!ActivityInfo.isResizeableMode(info.resizeMode)) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); - } - return false; - } - if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { - if (enableLogging) { - StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, - BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS); - } - return false; - } - return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; - } - /** * Whether the notification has been developer configured to bubble and is allowed by the user. */ @@ -620,7 +534,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe ENABLE_AUTO_BUBBLE_ALL, 0) != 0; } - private static boolean shouldUseContentIntent(Context context) { + static boolean shouldUseContentIntent(Context context) { return Settings.Secure.getInt(context.getContentResolver(), ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java new file mode 100644 index 000000000000..5002f5cce751 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -0,0 +1,70 @@ +/* + * 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 androidx.annotation.Nullable; + +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<>(); + + @Inject + BubbleData() {} + + /** + * The set of bubbles. + */ + public Collection<Bubble> getBubbles() { + return mBubbles.values(); + } + + @Nullable + public Bubble getBubble(String key) { + return mBubbles.get(key); + } + + public void addBubble(Bubble b) { + mBubbles.put(b.key, b); + } + + @Nullable + public Bubble removeBubble(String key) { + return mBubbles.remove(key); + } + + public void updateBubble(String key, NotificationEntry newEntry) { + Bubble oldBubble = mBubbles.get(key); + if (oldBubble != null) { + oldBubble.setEntry(newEntry); + } + } + + public void clear() { + mBubbles.clear(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 976a766dcc09..c2327ad41d3e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -16,19 +16,28 @@ package com.android.systemui.bubbles; +import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE; +import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS; + import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.annotation.Nullable; +import android.app.ActivityView; import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; @@ -39,16 +48,21 @@ import android.util.AttributeSet; import android.util.Log; import android.util.StatsLog; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.stack.ExpandableViewState; /** * Container for the expanded bubble view, handles rendering the caret and header of the view. @@ -68,8 +82,15 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList // Permission view private View mPermissionView; - // The view that is being displayed for the expanded state - private View mExpandedView; + // Views for expanded state + private ExpandableNotificationRow mNotifRow; + private ActivityView mActivityView; + + private boolean mActivityViewReady = false; + private PendingIntent mBubbleIntent; + + private int mMinHeight; + private int mHeaderHeight; private NotificationEntry mEntry; private PackageManager mPm; @@ -77,11 +98,40 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private Drawable mAppIcon; private INotificationManager mNotificationManagerService; + private BubbleController mBubbleController = Dependency.get(BubbleController.class); - // Need reference to let it know to collapse when new task is launched private BubbleStackView mStackView; - private OnBubbleBlockedListener mOnBubbleBlockedListener; + private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener; + + private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() { + @Override + public void onActivityViewReady(ActivityView view) { + if (!mActivityViewReady) { + mActivityViewReady = true; + mActivityView.startActivity(mBubbleIntent); + } + } + + @Override + public void onActivityViewDestroyed(ActivityView view) { + mActivityViewReady = false; + } + + /** + * This is only called for tasks on this ActivityView, which is also set to + * single-task mode -- meaning never more than one task on this display. If a task + * is being removed, it's the top Activity finishing and this bubble should + * be removed or collapsed. + */ + @Override + public void onTaskRemovalStarted(int taskId) { + if (mEntry != null) { + // Must post because this is called from a binder thread. + post(() -> mBubbleController.removeBubble(mEntry.key)); + } + } + }; public BubbleExpandedView(Context context) { this(context, null); @@ -99,6 +149,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mPm = context.getPackageManager(); + mMinHeight = getResources().getDimensionPixelSize( + R.dimen.bubble_expanded_default_height); try { mNotificationManagerService = INotificationManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE)); @@ -142,6 +194,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList viewWrapper.setLayoutTransition(transition); viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); + mHeaderHeight = getContext().getResources().getDimensionPixelSize( + R.dimen.bubble_expanded_header_height); mHeaderView = findViewById(R.id.header_layout); mHeaderTextView = findViewById(R.id.header_text); mDeepLinkIcon = findViewById(R.id.deep_link_button); @@ -152,6 +206,28 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPermissionView = findViewById(R.id.permission_layout); findViewById(R.id.no_bubbles_button).setOnClickListener(this); findViewById(R.id.yes_bubbles_button).setOnClickListener(this); + + mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, + true /* singleTaskInstance */); + addView(mActivityView); + + mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { + ActivityView activityView = (ActivityView) view; + // Here we assume that the position of the ActivityView on the screen + // remains regardless of IME status. When we move ActivityView, the + // forwardedInsets should be computed not against the current location + // and size, but against the post-moved location and size. + Point displaySize = new Point(); + view.getContext().getDisplay().getSize(displaySize); + int[] windowLocation = view.getLocationOnScreen(); + final int windowBottom = windowLocation[1] + view.getHeight(); + final int keyboardHeight = insets.getSystemWindowInsetBottom() + - insets.getStableInsetBottom(); + final int insetsBottom = Math.max(0, + windowBottom + keyboardHeight - displaySize.y); + activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); + return view.onApplyWindowInsets(insets); + }); } /** @@ -189,6 +265,29 @@ 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); + } + + /** + * Updates the entry backing this view. This will not re-populate ActivityView, it will + * only update the deep-links in the header, the title, and the height of the view. + */ + public void update(NotificationEntry entry) { + if (entry.key.equals(mEntry.key)) { + mEntry = entry; + updateHeaderView(); + updateHeight(); + } else { + Log.w(TAG, "Trying to update entry with different key, new entry: " + + entry.key + " old entry: " + mEntry.key); + } } private void updateHeaderView() { @@ -225,6 +324,56 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } } + private void updateExpandedView() { + mBubbleIntent = getBubbleIntent(mEntry); + if (mBubbleIntent != null) { + if (mNotifRow != null) { + // Clear out the row if we had it previously + removeView(mNotifRow); + mNotifRow = null; + } + mActivityView.setVisibility(VISIBLE); + } else { + // Hide activity view if we had it previously + mActivityView.setVisibility(GONE); + + // Use notification view + mNotifRow = mEntry.getRow(); + addView(mNotifRow); + } + updateView(); + } + + boolean performBackPressIfNeeded() { + if (mActivityView == null || !usingActivityView()) { + return false; + } + mActivityView.performBackPress(); + return true; + } + + void updateHeight() { + if (usingActivityView()) { + Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); + int desiredHeight; + if (data == null) { + // This is a contentIntent based bubble, lets allow it to be the max height + // as it was forced into this mode and not prepared to be small + desiredHeight = mStackView.getMaxExpandedHeight(); + } else { + desiredHeight = data.getDesiredHeight() > 0 + ? data.getDesiredHeight() + : mMinHeight; + } + int max = mStackView.getMaxExpandedHeight() - mHeaderHeight; + int height = Math.min(desiredHeight, max); + height = Math.max(height, mMinHeight); + LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams(); + lp.height = height; + mActivityView.setLayoutParams(lp); + } + } + @Override public void onClick(View view) { if (mEntry == null) { @@ -279,36 +428,88 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } /** + * Update appearance of the expanded view being displayed. + */ + public void updateView() { + if (usingActivityView() + && mActivityView.getVisibility() == VISIBLE + && mActivityView.isAttachedToWindow()) { + mActivityView.onLocationChanged(); + } else if (mNotifRow != null) { + applyRowState(mNotifRow); + } + updateHeight(); + } + + /** * Set the x position that the tip of the triangle should point to. */ - public void setPointerPosition(int x) { + public void setPointerPosition(float x) { // Adjust for the pointer size - x -= (mPointerView.getWidth() / 2); + x -= (mPointerView.getWidth() / 2f); mPointerView.setTranslationX(x); } /** - * Set the view to display for the expanded state. Passing null will clear the view. + * Removes and releases an ActivityView if one was previously created for this bubble. */ - public void setExpandedView(View view) { - if (mExpandedView == view) { + public void destroyActivityView(ViewGroup tmpParent) { + if (mActivityView == null) { return; } - if (mExpandedView != null) { - removeView(mExpandedView); + if (!mActivityViewReady) { + // release not needed, never initialized? + mActivityView = null; + return; } - mExpandedView = view; - if (mExpandedView != null) { - addView(mExpandedView); + // HACK: release() will crash if the view is not attached. + if (!isAttachedToWindow()) { + mActivityView.setVisibility(View.GONE); + tmpParent.addView(this); } + + mActivityView.release(); + ((ViewGroup) getParent()).removeView(this); + mActivityView = null; + mActivityViewReady = false; } - /** - * @return the view containing the expanded content, can be null. - */ - @Nullable - public View getExpandedView() { - return mExpandedView; + private boolean usingActivityView() { + return mBubbleIntent != null; + } + + private void applyRowState(ExpandableNotificationRow view) { + view.reset(); + view.setHeadsUp(false); + view.resetTranslation(); + view.setOnKeyguard(false); + view.setOnAmbient(false); + view.setClipBottomAmount(0); + view.setClipTopAmount(0); + view.setContentTransformationAmount(0, false); + view.setIconsVisible(true); + + // TODO - Need to reset this (and others) when view goes back in shade, leave for now + // view.setTopRoundness(1, false); + // view.setBottomRoundness(1, false); + + ExpandableViewState viewState = view.getViewState(); + viewState = viewState == null ? new ExpandableViewState() : viewState; + viewState.height = view.getIntrinsicHeight(); + viewState.gone = false; + viewState.hidden = false; + viewState.dimmed = false; + viewState.dark = false; + viewState.alpha = 1f; + viewState.notGoneIndex = -1; + viewState.xTranslation = 0; + viewState.yTranslation = 0; + viewState.zTranslation = 0; + viewState.scaleX = 1; + viewState.scaleY = 1; + viewState.inShelf = true; + viewState.headsUpIsVisible = false; + viewState.applyToView(view); } private Intent getSettingsIntent(String packageName, final int appUid) { @@ -320,6 +521,61 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return intent; } + @Nullable + private PendingIntent getBubbleIntent(NotificationEntry entry) { + Notification notif = entry.notification.getNotification(); + String packageName = entry.notification.getPackageName(); + Notification.BubbleMetadata data = notif.getBubbleMetadata(); + if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */, + packageName)) { + return data.getIntent(); + } else if (BubbleController.shouldUseContentIntent(mContext) + && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */, + packageName)) { + return notif.contentIntent; + } + return null; + } + + /** + * Whether an intent is properly configured to display in an {@link android.app.ActivityView}. + * + * @param intent the pending intent of the bubble. + * @param enableLogging whether bubble developer error should be logged. + * @param packageName the notification package name for this bubble. + * @return + */ + private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging, + String packageName) { + if (intent == null) { + return false; + } + ActivityInfo info = + intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0); + if (info == null) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING); + } + return false; + } + if (!ActivityInfo.isResizeableMode(info.resizeMode)) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE); + } + return false; + } + if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) { + if (enableLogging) { + StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName, + BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS); + } + return false; + } + return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0; + } + /** * Listener that is notified when a bubble is blocked. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 2b344f6cf195..5546e4c50174 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -19,7 +19,6 @@ package com.android.systemui.bubbles; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import android.app.ActivityView; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; @@ -33,13 +32,11 @@ import android.util.StatsLog; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; import android.widget.FrameLayout; -import android.widget.LinearLayout; import androidx.annotation.MainThread; import androidx.annotation.Nullable; @@ -54,8 +51,6 @@ import com.android.systemui.bubbles.animation.ExpandedAnimationController; import com.android.systemui.bubbles.animation.PhysicsAnimationLayout; import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import java.math.BigDecimal; import java.math.RoundingMode; @@ -63,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"; /** @@ -90,27 +85,34 @@ 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; private ExpandedAnimationController mExpandedAnimationController; - private BubbleExpandedView mExpandedViewContainer; + private FrameLayout mExpandedViewContainer; + private int mBubbleSize; private int mBubblePadding; private int mExpandedAnimateXDistance; private int mExpandedAnimateYDistance; + private int mStatusBarHeight; + private int mPipDismissHeight; + private Bubble mExpandedBubble; private boolean mIsExpanded; - private int mExpandedBubbleHeight; + private BubbleTouchHandler mTouchHandler; - private BubbleView mExpandedBubble; private BubbleController.BubbleExpandListener mExpandListener; + private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener; private boolean mViewUpdatedRequested = false; private boolean mIsAnimating = false; + private LayoutInflater mInflater; + // Used for determining view / touch intersection int[] mTempLoc = new int[2]; RectF mTempRect = new RectF(); @@ -140,11 +142,14 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } }; - public BubbleStackView(Context context) { + public BubbleStackView(Context context, BubbleData data) { super(context); - mTouchHandler = new BubbleTouchHandler(context); + mBubbleData = data; + mInflater = LayoutInflater.from(context); + mTouchHandler = new BubbleTouchHandler(context, this); setOnTouchListener(mTouchHandler); + mInflater = LayoutInflater.from(context); Resources res = getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); @@ -153,8 +158,11 @@ 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); + mPipDismissHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.pip_dismiss_gradient_height); - mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mDisplaySize = new Point(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getSize(mDisplaySize); @@ -174,9 +182,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - mExpandedViewContainer = (BubbleExpandedView) - LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view, - this /* parent */, false /* attachToRoot */); + mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); mExpandedViewContainer.setPadding(padding, padding, padding, padding); mExpandedViewContainer.setClipChildren(false); @@ -197,6 +203,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); setClipChildren(false); + + mBubbleContainer.bringToFront(); } @Override @@ -218,6 +226,17 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } /** + * Updates the visibility of the 'dot' indicating an update on the bubble. + * @param key the {@link NotificationEntry#key} associated with the bubble. + */ + public void updateDotVisibility(String key) { + Bubble b = mBubbleData.getBubble(key); + if (b != null) { + b.iconView.updateDotVisibility(); + } + } + + /** * Sets the listener to notify when the bubble stack is expanded. */ public void setExpandListener(BubbleController.BubbleExpandListener listener) { @@ -228,7 +247,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * Sets the listener to notify when a bubble is blocked. */ public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) { - mExpandedViewContainer.setOnBlockedListener(listener); + mBlockedListener = listener; + for (Bubble b : mBubbleData.getBubbles()) { + b.expandedView.setOnBlockedListener(mBlockedListener); + } } /** @@ -241,19 +263,29 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * The {@link BubbleView} that is expanded, null if one does not exist. */ - public BubbleView getExpandedBubble() { + BubbleView getExpandedBubbleView() { + return mExpandedBubble != null ? mExpandedBubble.iconView : null; + } + + /** + * The {@link Bubble} that is expanded, null if one does not exist. + */ + Bubble getExpandedBubble() { return mExpandedBubble; } /** * Sets the bubble that should be expanded and expands if needed. + * + * @param key the {@link NotificationEntry#key} associated with the bubble to expand. */ - public void setExpandedBubble(BubbleView bubbleToExpand) { + void setExpandedBubble(String key) { + Bubble bubbleToExpand = mBubbleData.getBubble(key); if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) { // Previously expanded, notify that this bubble is no longer expanded - notifyExpansionChanged(mExpandedBubble, false /* expanded */); + notifyExpansionChanged(mExpandedBubble.entry, false /* expanded */); } - BubbleView prevBubble = mExpandedBubble; + Bubble prevBubble = mExpandedBubble; mExpandedBubble = bubbleToExpand; if (!mIsExpanded) { // If we weren't previously expanded we should animate open. @@ -268,8 +300,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED); } - mExpandedBubble.getEntry().setShowInShadeWhenBubble(false); - notifyExpansionChanged(mExpandedBubble, true /* expanded */); + mExpandedBubble.entry.setShowInShadeWhenBubble(false); + notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */); } /** @@ -280,7 +312,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); if (entry.equals(bv.getEntry())) { - setExpandedBubble(bv); + setExpandedBubble(entry.key); } } } @@ -288,48 +320,67 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Adds a bubble to the top of the stack. * - * @param bubbleView the view to add to the stack. + * @param entry the notification to add to the stack of bubbles. */ - public void addBubble(BubbleView bubbleView) { - mBubbleContainer.addView(bubbleView, 0, + public void addBubble(NotificationEntry entry) { + Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener); + mBubbleData.addBubble(b); + + mBubbleContainer.addView(b.iconView, 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters); + ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters); + requestUpdate(); - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); } /** * Remove a bubble from the stack. */ - public void removeBubble(BubbleView bubbleView) { - int removedIndex = mBubbleContainer.indexOfChild(bubbleView); - mBubbleContainer.removeView(bubbleView); + public void removeBubble(String key) { + Bubble b = mBubbleData.removeBubble(key); + if (b == null) { + return; + } + b.entry.setBubbleDismissed(true); + + // Remove it from the views + int removedIndex = mBubbleContainer.indexOfChild(b.iconView); + b.expandedView.destroyActivityView(this /* tmpParent */); + mBubbleContainer.removeView(b.iconView); + int bubbleCount = mBubbleContainer.getChildCount(); if (bubbleCount == 0) { // If no bubbles remain, collapse the entire stack. collapseStack(); return; - } else if (bubbleView.equals(mExpandedBubble)) { + } else if (b.equals(mExpandedBubble)) { // Was the current bubble just removed? // If we have other bubbles and are expanded go to the next one or previous // if the bubble removed was last int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1; BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex); if (mIsExpanded) { - setExpandedBubble(expandedBubble); + setExpandedBubble(expandedBubble.getKey()); } else { mExpandedBubble = null; } } - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); } /** * Dismiss the stack of bubbles. */ public void stackDismissed() { + for (Bubble bubble : mBubbleData.getBubbles()) { + bubble.entry.setBubbleDismissed(true); + bubble.expandedView.destroyActivityView(this /* tmpParent */); + } + mBubbleData.clear(); collapseStack(); mBubbleContainer.removeAllViews(); + mExpandedViewContainer.removeAllViews(); logBubbleEvent(null /* no bubble associated with bubble stack dismiss */, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED); } @@ -337,25 +388,25 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Updates a bubble in the stack. * - * @param bubbleView the view to update in the stack. - * @param entry the entry to update it with. + * @param entry the entry to update in the stack. * @param updatePosition whether this bubble should be moved to top of the stack. */ - public void updateBubble(BubbleView bubbleView, NotificationEntry entry, - boolean updatePosition) { - bubbleView.update(entry); + public void updateBubble(NotificationEntry entry, boolean updatePosition) { + Bubble b = mBubbleData.getBubble(entry.key); + mBubbleData.updateBubble(entry.key, entry); + if (updatePosition && !mIsExpanded) { // If alerting it gets promoted to top of the stack. - if (mBubbleContainer.indexOfChild(bubbleView) != 0) { - mBubbleContainer.moveViewTo(bubbleView, 0); + if (mBubbleContainer.indexOfChild(b.iconView) != 0) { + mBubbleContainer.moveViewTo(b.iconView, 0); } requestUpdate(); } - if (mIsExpanded && bubbleView.equals(mExpandedBubble)) { + if (mIsExpanded && entry.equals(mExpandedBubble.entry)) { entry.setShowInShadeWhenBubble(false); requestUpdate(); } - logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); + logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); } /** @@ -393,7 +444,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F if (mIsExpanded) { // TODO: Save opened bubble & move it to top of stack animateExpansion(false /* shouldExpand */); - notifyExpansionChanged(mExpandedBubble, mIsExpanded); + notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); } } @@ -412,8 +463,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F @MainThread public void expandStack() { if (!mIsExpanded) { - mExpandedBubble = getTopBubble(); - setExpandedBubble(mExpandedBubble); + String expandedBubbleKey = getBubbleAt(0).getKey(); + setExpandedBubble(expandedBubbleKey); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED); } } @@ -438,9 +489,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(); @@ -459,7 +511,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F final float yStart = Math.min( mStackAnimationController.getStackPosition().y, mExpandedAnimateYDistance); - final float yDest = getStatusBarHeight() + mExpandedBubble.getHeight() + mBubblePadding; + final float yDest = getStatusBarHeight() + + mExpandedBubble.iconView.getHeight() + mBubblePadding; if (shouldExpand) { mExpandedViewContainer.setTranslationX(xStart); @@ -484,17 +537,12 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F + mBubbleContainer.getPaddingStart(); } - private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) { + private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) { if (mExpandListener != null) { - NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null; mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null); } } - private BubbleView getTopBubble() { - return getBubbleAt(0); - } - /** Return the BubbleView at the given index from the bubble container. */ public BubbleView getBubbleAt(int i) { return mBubbleContainer.getChildCount() > i @@ -502,55 +550,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; } @@ -613,6 +655,20 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } /** + * Calculates how large the expanded view of the bubble can be. This takes into account the + * y position when the bubbles are expanded as well as the bounds of the dismiss target. + */ + int getMaxExpandedHeight() { + int expandedY = (int) mExpandedAnimationController.getExpandedY(); + int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null + ? mBubbleContainer.getChildAt(0).getHeight() + : 0; + // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset + int pipDismissHeight = mPipDismissHeight - getBottomInset(); + return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight; + } + + /** * Minimum velocity, in pixels/second, required to get from x to destX while being slowed by a * given frictional force. * @@ -639,7 +695,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); @@ -648,6 +704,14 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F return 0; } + private int getBottomInset() { + if (getRootWindowInsets() != null) { + WindowInsets insets = getRootWindowInsets(); + return insets.getSystemWindowInsetBottom(); + } + return 0; + } + private boolean isIntersecting(View view, float x, float y) { mTempLoc = view.getLocationOnScreen(); mTempRect.set(mTempLoc[0], mTempLoc[1], mTempLoc[0] + view.getWidth(), @@ -665,31 +729,11 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F } private void updateExpandedBubble() { - if (mExpandedBubble == null) { - return; - } - - mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this); - if (mExpandedBubble.hasAppOverlayIntent()) { - // Bubble with activity view expanded state - ActivityView expandedView = mExpandedBubble.getActivityView(); - // XXX: gets added to linear layout - expandedView.setLayoutParams(new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight)); - - mExpandedViewContainer.setExpandedView(expandedView); - } else { - // Bubble with notification view expanded state - ExpandableNotificationRow row = mExpandedBubble.getRowView(); - if (row.getParent() != null) { - // Row might still be in the shade when we expand - ((ViewGroup) row.getParent()).removeView(row); - } - if (mIsExpanded) { - mExpandedViewContainer.setExpandedView(row); - } else { - mExpandedViewContainer.setExpandedView(null); - } + mExpandedViewContainer.removeAllViews(); + if (mExpandedBubble != null && mIsExpanded) { + mExpandedViewContainer.addView(mExpandedBubble.expandedView); + mExpandedBubble.expandedView.populateActivityView(); + mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); } } @@ -697,18 +741,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); - if (!mIsExpanded) { - mExpandedViewContainer.setExpandedView(null); - } else { - View expandedView = mExpandedViewContainer.getExpandedView(); - if (expandedView instanceof ActivityView) { - if (expandedView.isAttachedToWindow()) { - ((ActivityView) expandedView).onLocationChanged(); - } - } else { - applyRowState(mExpandedBubble.getRowView()); - } + if (mIsExpanded) { + mExpandedBubble.expandedView.updateView(); } + int bubbsCount = mBubbleContainer.getChildCount(); for (int i = 0; i < bubbsCount; i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); @@ -730,44 +766,10 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F private void updatePointerPosition() { if (mExpandedBubble != null) { - float pointerPosition = mExpandedBubble.getPosition().x - + (mExpandedBubble.getWidth() / 2f); - mExpandedViewContainer.setPointerPosition((int) pointerPosition); - } - } - - private void applyRowState(ExpandableNotificationRow view) { - view.reset(); - view.setHeadsUp(false); - view.resetTranslation(); - view.setOnKeyguard(false); - view.setOnAmbient(false); - view.setClipBottomAmount(0); - view.setClipTopAmount(0); - view.setContentTransformationAmount(0, false); - view.setIconsVisible(true); - - // TODO - Need to reset this (and others) when view goes back in shade, leave for now - // view.setTopRoundness(1, false); - // view.setBottomRoundness(1, false); - - ExpandableViewState viewState = view.getViewState(); - viewState = viewState == null ? new ExpandableViewState() : viewState; - viewState.height = view.getIntrinsicHeight(); - viewState.gone = false; - viewState.hidden = false; - viewState.dimmed = false; - viewState.dark = false; - viewState.alpha = 1f; - viewState.notGoneIndex = -1; - viewState.xTranslation = 0; - viewState.yTranslation = 0; - viewState.zTranslation = 0; - viewState.scaleX = 1; - viewState.scaleY = 1; - viewState.inShelf = true; - viewState.headsUpIsVisible = false; - viewState.applyToView(view); + float pointerPosition = mExpandedBubble.iconView.getTranslationX() + + (mExpandedBubble.iconView.getWidth() / 2f); + mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition); + } } /** @@ -780,19 +782,19 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F /** * Finds the bubble index within the stack. * - * @param bubbleView the view of the bubble. + * @param bubble the bubble to look up. * @return the index of the bubble view within the bubble stack. The range of the position * is between 0 and the bubble count minus 1. */ - public int getBubbleIndex(BubbleView bubbleView) { - return mBubbleContainer.indexOfChild(bubbleView); + int getBubbleIndex(Bubble bubble) { + return mBubbleContainer.indexOfChild(bubble.iconView); } /** * @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(); } @@ -801,7 +803,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(); } @@ -813,7 +815,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F * the user interaction is not specific to one bubble. * @param action the user interaction enum. */ - private void logBubbleEvent(@Nullable BubbleView bubble, int action) { + private void logBubbleEvent(@Nullable Bubble bubble, int action) { if (bubble == null) { StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, null /* package name */, @@ -825,7 +827,7 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F getNormalizedXPosition(), getNormalizedYPosition()); } else { - StatusBarNotification notification = bubble.getEntry().notification; + StatusBarNotification notification = bubble.entry.notification; StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, notification.getPackageName(), notification.getNotification().getChannelId(), @@ -837,4 +839,15 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F getNormalizedYPosition()); } } + + /** + * Called when a back gesture should be directed to the Bubbles stack. When expanded, + * a back key down/up event pair is forwarded to the bubble Activity. + */ + boolean performBackPressIfNeeded() { + if (!isExpanded()) { + return false; + } + return mExpandedBubble.expandedView.performBackPressIfNeeded(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index 22cd2fcc3e72..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.getExpandedBubble())) { - 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); + 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 74ddc8f6b8fc..b409a3181e2b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -17,30 +17,19 @@ package com.android.systemui.bubbles; import android.annotation.Nullable; -import android.app.ActivityView; import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; -import android.graphics.Insets; -import android.graphics.Point; -import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.graphics.ColorUtils; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -49,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 @@ -62,11 +51,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati private int mIconInset; private NotificationEntry mEntry; - private PendingIntent mAppOverlayIntent; - private BubbleController mBubbleController; - private ActivityView mActivityView; - private boolean mActivityViewReady; - private boolean mActivityViewStarted; public BubbleView(Context context) { this(context, null); @@ -86,7 +70,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati // XXX: can this padding just be on the view and we look it up? mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding); mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); - mBubbleController = Dependency.get(BubbleController.class); } @Override @@ -168,7 +151,6 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati updateViews(); } - /** * @return the {@link ExpandableNotificationRow} view to display notification content when the * bubble is expanded. @@ -234,123 +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); } - - /** - * @return a view used to display app overlay content when expanded. - */ - public ActivityView getActivityView() { - if (mActivityView == null) { - mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, - true /* singleTaskInstance */); - Log.d(TAG, "[getActivityView] created: " + mActivityView); - mActivityView.setCallback(new ActivityView.StateCallback() { - @Override - public void onActivityViewReady(ActivityView view) { - mActivityViewReady = true; - mActivityView.startActivity(mAppOverlayIntent); - } - - @Override - public void onActivityViewDestroyed(ActivityView view) { - mActivityViewReady = false; - } - - /** - * This is only called for tasks on this ActivityView, which is also set to - * single-task mode -- meaning never more than one task on this display. If a task - * is being removed, it's the top Activity finishing and this bubble should - * be removed or collapsed. - */ - @Override - public void onTaskRemovalStarted(int taskId) { - if (mEntry != null) { - // Must post because this is called from a binder thread. - post(() -> mBubbleController.removeBubble(mEntry.key)); - } - } - }); - mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { - ActivityView activityView = (ActivityView) view; - // Here we assume that the position of the ActivityView on the screen - // remains regardless of IME status. When we move ActivityView, the - // forwardedInsets should be computed not against the current location - // and size, but against the post-moved location and size. - Point displaySize = new Point(); - view.getContext().getDisplay().getSize(displaySize); - int[] windowLocation = view.getLocationOnScreen(); - final int windowBottom = windowLocation[1] + view.getHeight(); - final int keyboardHeight = insets.getSystemWindowInsetBottom() - - insets.getStableInsetBottom(); - final int insetsBottom = Math.max(0, - windowBottom + keyboardHeight - displaySize.y); - activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); - return view.onApplyWindowInsets(insets); - }); - - } - return mActivityView; - } - - /** - * Removes and releases an ActivityView if one was previously created for this bubble. - */ - public void destroyActivityView(ViewGroup tmpParent) { - if (mActivityView == null) { - return; - } - if (!mActivityViewReady) { - // release not needed, never initialized? - mActivityView = null; - return; - } - // HACK: release() will crash if the view is not attached. - if (!mActivityView.isAttachedToWindow()) { - mActivityView.setVisibility(View.GONE); - tmpParent.addView(mActivityView, new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - } - - mActivityView.release(); - - ((ViewGroup) mActivityView.getParent()).removeView(mActivityView); - mActivityView = null; - } - - @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()); - } - - /** - * @return whether an ActivityView should be used to display the content of this Bubble - */ - public boolean hasAppOverlayIntent() { - return mAppOverlayIntent != null; - } - - public PendingIntent getAppOverlayIntent() { - return mAppOverlayIntent; - - } - - public void setBubbleIntent(PendingIntent intent) { - mAppOverlayIntent = intent; - } } 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..f0d9be1e484a 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(); + public float getExpandedY() { + final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null; 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/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index 900ea72d856c..a74c3287e1f1 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -61,7 +61,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { @VisibleForTesting public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) { - super(context, type); + super(context, type, false /* immediately */); mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context); mWpHiddenColors = new GradientColors(); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java index 4a2e06c058cb..f5cf149ba868 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java @@ -25,9 +25,4 @@ public interface DozeReceiver { * Invoked every time a minute is elapsed in doze mode */ void dozeTimeTick(); - - /** - * When view is double tapped in doze mode. - */ - void onDozeDoubleTap(); } 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/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 873fbc226629..e207cb13d311 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -161,7 +161,8 @@ public class DozeTriggers implements DozeMachine.Part { } else { mDozeHost.extendPulse(); } - }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason); + }, sensorPerformedProxCheck + || (mDockManager != null && mDockManager.isDocked()), pulseReason); } if (isPickup) { @@ -224,7 +225,9 @@ public class DozeTriggers implements DozeMachine.Part { case INITIALIZED: mBroadcastReceiver.register(mContext); mDozeHost.addCallback(mHostCallback); - mDockManager.addListener(mDockEventListener); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + } checkTriggersAtInit(); break; case DOZE: @@ -250,7 +253,9 @@ public class DozeTriggers implements DozeMachine.Part { case FINISH: mBroadcastReceiver.unregister(mContext); mDozeHost.removeCallback(mHostCallback); - mDockManager.removeListener(mDockEventListener); + if (mDockManager != null) { + mDockManager.removeListener(mDockEventListener); + } mDozeSensors.setListening(false); mDozeSensors.setProxListening(false); break; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index f5ac0d39d61f..3fa6035387c7 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -91,6 +91,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; @@ -1091,7 +1092,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 +1466,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 +1506,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 +1526,14 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, return true; } }); - setTitle(R.string.global_actions); + mGlobalActionsLayout.setRotationListener(this::onRotate); } 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 +1552,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; @@ -1664,5 +1683,19 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, public void setKeyguardShowing(boolean keyguardShowing) { mKeyguardShowing = keyguardShowing; } + + public void onRotate(int from, int to) { + if (mShowing && isGridEnabled(mContext)) { + initializeLayout(); + updateList(); + } + } + } + + /** + * 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/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 684175cf4212..85d975f36db5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -25,7 +25,9 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; @@ -35,10 +37,13 @@ import android.os.Handler; import android.os.Trace; import android.provider.Settings; import android.service.notification.ZenModeConfig; -import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; import android.text.style.StyleSpan; +import android.util.MathUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -72,6 +77,8 @@ public class KeyguardSliceProvider extends SliceProvider implements private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD); public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main"; + private static final String KEYGUARD_HEADER_URI = + "content://com.android.systemui.keyguard/header"; public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date"; public static final String KEYGUARD_NEXT_ALARM_URI = "content://com.android.systemui.keyguard/alarm"; @@ -90,6 +97,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private static KeyguardSliceProvider sInstance; protected final Uri mSliceUri; + protected final Uri mHeaderUri; protected final Uri mDateUri; protected final Uri mAlarmUri; protected final Uri mDndUri; @@ -163,6 +171,7 @@ public class KeyguardSliceProvider extends SliceProvider implements KeyguardSliceProvider(Handler handler) { mHandler = handler; mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); + mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI); mDateUri = Uri.parse(KEYGUARD_DATE_URI); mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI); mDndUri = Uri.parse(KEYGUARD_DND_URI); @@ -213,26 +222,32 @@ public class KeyguardSliceProvider extends SliceProvider implements protected void addMediaLocked(ListBuilder listBuilder) { if (mMediaMetaData != null) { SpannableStringBuilder builder = new SpannableStringBuilder(); + + Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); + if (notificationIcon != null) { + Drawable drawable = notificationIcon.loadDrawable(getContext()); + Rect mediaBounds = new Rect(0 /* left */, 0 /* top */, + drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + int iconHeaderSize = getContext().getResources() + .getDimensionPixelSize(R.dimen.header_icon_size); + MathUtils.fitRect(mediaBounds, iconHeaderSize); + drawable.setBounds(mediaBounds); + builder.append("# "); + builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), + 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); if (TextUtils.isEmpty(title)) { title = getContext().getResources().getString(R.string.music_controls_no_title); } builder.append(title); - builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder)); CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); if (!TextUtils.isEmpty(album)) { - builder.append(" ").append(album); + listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album)); } - - RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder); - Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); - if (notificationIcon != null) { - IconCompat icon = IconCompat.createFromIcon(notificationIcon); - mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE); - } - - listBuilder.addRow(mediaBuilder); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index df763151cdd7..9bca2cc434ac 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -40,19 +40,15 @@ import android.text.style.StyleSpan; import android.util.Log; import android.view.Window; import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.CompoundButton; import com.android.systemui.R; public class MediaProjectionPermissionActivity extends Activity - implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener, - DialogInterface.OnCancelListener { + implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { private static final String TAG = "MediaProjectionPermissionActivity"; private static final float MAX_APP_NAME_SIZE_PX = 500f; private static final String ELLIPSIS = "\u2026"; - private boolean mPermanentGrant; private String mPackageName; private int mUid; private IMediaProjectionManager mService; @@ -85,8 +81,7 @@ public class MediaProjectionPermissionActivity extends Activity try { if (mService.hasProjectionPermission(mUid, mPackageName)) { - setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName, - false /*permanentGrant*/)); + setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName)); finish(); return; } @@ -136,19 +131,20 @@ public class MediaProjectionPermissionActivity extends Activity appNameIndex, appNameIndex + appName.length(), 0); } + String dialogTitle = getString(R.string.media_projection_dialog_title, appName); + mDialog = new AlertDialog.Builder(this) + .setTitle(dialogTitle) .setIcon(aInfo.loadIcon(packageManager)) .setMessage(message) .setPositiveButton(R.string.media_projection_action_text, this) .setNegativeButton(android.R.string.cancel, this) - .setView(R.layout.remember_permission_checkbox) .setOnCancelListener(this) .create(); mDialog.create(); mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); - ((CheckBox) mDialog.findViewById(R.id.remember)).setOnCheckedChangeListener(this); final Window w = mDialog.getWindow(); w.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); @@ -168,8 +164,7 @@ public class MediaProjectionPermissionActivity extends Activity public void onClick(DialogInterface dialog, int which) { try { if (which == AlertDialog.BUTTON_POSITIVE) { - setResult(RESULT_OK, getMediaProjectionIntent( - mUid, mPackageName, mPermanentGrant)); + setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName)); } } catch (RemoteException e) { Log.e(TAG, "Error granting projection permission", e); @@ -182,15 +177,10 @@ public class MediaProjectionPermissionActivity extends Activity } } - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mPermanentGrant = isChecked; - } - - private Intent getMediaProjectionIntent(int uid, String packageName, boolean permanentGrant) + private Intent getMediaProjectionIntent(int uid, String packageName) throws RemoteException { IMediaProjection projection = mService.createProjection(uid, packageName, - MediaProjectionManager.TYPE_SCREEN_CAPTURE, permanentGrant); + MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */); Intent intent = new Intent(); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder()); return intent; 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 faeacf767c9a..625eacd7e2a7 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -26,16 +26,26 @@ import android.os.Handler import android.os.UserHandle import android.os.UserManager import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.Dependency +import com.android.systemui.Dependency.BG_HANDLER_NAME +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.R +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 import javax.inject.Singleton @Singleton -class PrivacyItemController @Inject constructor(private val context: Context) { +class PrivacyItemController @Inject constructor( + val context: Context, + 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, @@ -48,16 +58,13 @@ class PrivacyItemController @Inject constructor(private val context: Context) { const val TAG = "PrivacyItemController" const val SYSTEM_UID = 1000 } - private var privacyList = emptyList<PrivacyItem>() - @Suppress("DEPRECATION") - private val appOpsController = Dependency.get(AppOpsController::class.java) + @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>() - @Suppress("DEPRECATION") - private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER)) - @Suppress("DEPRECATION") - private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER) private var listening = false val systemApp = PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context) @@ -188,4 +195,22 @@ class PrivacyItemController @Inject constructor(private val context: Context) { 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/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 7a7d1f6b1f04..bb34a878a0d8 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -461,6 +461,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, if (mSnapAlgorithm == null) { mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide); + if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) { + mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget(); + } } if (mMinimizedSnapAlgorithm == null) { mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), 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/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 01b0bb14c7ca..110d51563e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -348,15 +348,13 @@ public class NotificationShelf extends ActivatableNotificationView implements if (notGoneIndex == 0) { StatusBarIconView icon = row.getEntry().expandedIcon; NotificationIconContainer.IconState iconState = getIconState(icon); + // The icon state might be null in rare cases where the notification is actually + // added to the layout, but not to the shelf. An example are replied messages, since + // they don't show up on AOD if (iconState != null && iconState.clampedAppearAmount == 1.0f) { // only if the first icon is fully in the shelf we want to clip to it! backgroundTop = (int) (row.getTranslationY() - getTranslationY()); firstElementRoundness = row.getCurrentTopRoundness(); - } else if (iconState == null) { - Log.wtf(TAG, "iconState is null. ExpandedIcon: " + row.getEntry().expandedIcon - + (row.getEntry().expandedIcon != null - ? "\n icon parent: " + row.getEntry().expandedIcon.getParent() : "") - + " \n number of notifications: " + mHostLayout.getChildCount() ); } } if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) { 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..b65c4a5f71d8 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, @@ -125,9 +125,10 @@ public class NotificationInflater { private boolean mIsChildInGroup; private InflationCallback mCallback; private boolean mRedactAmbient; + private boolean mInflateSynchronously = false; private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>(); - public NotificationInflater(ExpandableNotificationRow row) { + public NotificationContentInflater(ExpandableNotificationRow row) { mRow = row; } @@ -232,8 +233,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()) { @@ -249,10 +249,20 @@ public class NotificationInflater { // To check if the notification has inline image and preload inline image if necessary. mRow.getImageResolver().preloadImages(sbn.getNotification()); - AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews, - mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, - mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler); - if (mCallback != null && mCallback.doInflateSynchronous()) { + AsyncInflationTask task = new AsyncInflationTask( + sbn, + mInflateSynchronously, + reInflateFlags, + mCachedContentViews, + mRow, + mIsLowPriority, + mIsChildInGroup, + mUsesIncreasedHeight, + mUsesIncreasedHeadsUpHeight, + mRedactAmbient, + mCallback, + mRemoteViewClickHandler); + if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { task.execute(); @@ -260,13 +270,23 @@ public class NotificationInflater { } @VisibleForTesting - InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags, - Notification.Builder builder, Context packageContext) { + InflationProgress inflateNotificationViews( + boolean inflateSynchronously, + @InflationFlag int reInflateFlags, + Notification.Builder builder, + Context packageContext) { InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); - apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient, - mRemoteViewClickHandler, null); + apply( + inflateSynchronously, + result, + reInflateFlags, + mCachedContentViews, + mRow, + mRedactAmbient, + mRemoteViewClickHandler, + null); return result; } @@ -349,9 +369,13 @@ public class NotificationInflater { return result; } - public static CancellationSignal apply(InflationProgress result, - @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews, - ExpandableNotificationRow row, boolean redactAmbient, + public static CancellationSignal apply( + boolean inflateSynchronously, + InflationProgress result, + @InflationFlag int reInflateFlags, + ArrayMap<Integer, RemoteViews> cachedContentViews, + ExpandableNotificationRow row, + boolean redactAmbient, RemoteViews.OnClickHandler remoteViewClickHandler, @Nullable InflationCallback callback) { NotificationContentView privateLayout = row.getPrivateLayout(); @@ -374,8 +398,8 @@ public class NotificationInflater { return result.newContentView; } }; - applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient, - isNewView, remoteViewClickHandler, callback, privateLayout, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews, + row, redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout, privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); @@ -398,9 +422,9 @@ public class NotificationInflater { return result.newExpandedView; } }; - applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, - privateLayout, privateLayout.getExpandedChild(), + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, + cachedContentViews, row, redactAmbient, isNewView, remoteViewClickHandler, + callback, privateLayout, privateLayout.getExpandedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations, applyCallback); @@ -424,9 +448,9 @@ public class NotificationInflater { return result.newHeadsUpView; } }; - applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, - privateLayout, privateLayout.getHeadsUpChild(), + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, + cachedContentViews, row, redactAmbient, isNewView, remoteViewClickHandler, + callback, privateLayout, privateLayout.getHeadsUpChild(), privateLayout.getVisibleWrapper( VISIBLE_TYPE_HEADSUP), runningInflations, applyCallback); @@ -449,8 +473,8 @@ public class NotificationInflater { return result.newPublicView; } }; - applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews, + row, redactAmbient, isNewView, remoteViewClickHandler, callback, publicLayout, publicLayout.getContractedChild(), publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); @@ -473,8 +497,8 @@ public class NotificationInflater { return result.newAmbientView; } }; - applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, - redactAmbient, isNewView, remoteViewClickHandler, callback, + applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews, + row, redactAmbient, isNewView, remoteViewClickHandler, callback, newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations, applyCallback); @@ -490,18 +514,24 @@ public class NotificationInflater { } @VisibleForTesting - static void applyRemoteView(final InflationProgress result, - final @InflationFlag int reInflateFlags, @InflationFlag int inflationId, + static void applyRemoteView( + boolean inflateSynchronously, + final InflationProgress result, + final @InflationFlag int reInflateFlags, + @InflationFlag int inflationId, final ArrayMap<Integer, RemoteViews> cachedContentViews, - final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView, + final ExpandableNotificationRow row, + final boolean redactAmbient, + boolean isNewView, RemoteViews.OnClickHandler remoteViewClickHandler, @Nullable final InflationCallback callback, - NotificationContentView parentLayout, View existingView, + NotificationContentView parentLayout, + View existingView, NotificationViewWrapper existingWrapper, final HashMap<Integer, CancellationSignal> runningInflations, ApplyCallback applyCallback) { RemoteViews newContentView = applyCallback.getRemoteView(); - if (callback != null && callback.doInflateSynchronous()) { + if (inflateSynchronously) { try { if (isNewView) { View v = newContentView.apply( @@ -724,15 +754,7 @@ public class NotificationInflater { * @param entry the entry with the content views set * @param inflatedFlags the flags associated with the content views that were inflated */ - void onAsyncInflationFinished(NotificationEntry entry, - @InflationFlag int inflatedFlags); - - /** - * Used to disable async-ness for tests. Should only be used for tests. - */ - default boolean doInflateSynchronous() { - return false; - } + void onAsyncInflationFinished(NotificationEntry entry, @InflationFlag int inflatedFlags); } public void clearCachesAndReInflate() { @@ -740,6 +762,15 @@ public class NotificationInflater { inflateNotificationViews(); } + /** + * Sets whether to perform inflation on the same thread as the caller. This method should only + * be used in tests, not in production. + */ + @VisibleForTesting + void setInflateSynchronously(boolean inflateSynchronously) { + mInflateSynchronously = inflateSynchronously; + } + private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) { NotificationContentView ambientView = redactAmbient ? row.getPublicLayout() : row.getPrivateLayout(); @@ -751,6 +782,7 @@ public class NotificationInflater { private final StatusBarNotification mSbn; private final Context mContext; + private final boolean mInflateSynchronously; private final boolean mIsLowPriority; private final boolean mIsChildInGroup; private final boolean mUsesIncreasedHeight; @@ -764,14 +796,22 @@ public class NotificationInflater { private RemoteViews.OnClickHandler mRemoteViewClickHandler; private CancellationSignal mCancellationSignal; - private AsyncInflationTask(StatusBarNotification notification, + private AsyncInflationTask( + StatusBarNotification notification, + boolean inflateSynchronously, @InflationFlag int reInflateFlags, - ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row, - boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight, - boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, - InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler) { + ArrayMap<Integer, RemoteViews> cachedContentViews, + ExpandableNotificationRow row, + boolean isLowPriority, + boolean isChildInGroup, + boolean usesIncreasedHeight, + boolean usesIncreasedHeadsUpHeight, + boolean redactAmbient, + InflationCallback callback, + RemoteViews.OnClickHandler remoteViewClickHandler) { mRow = row; mSbn = notification; + mInflateSynchronously = inflateSynchronously; mReInflateFlags = reInflateFlags; mCachedContentViews = cachedContentViews; mContext = mRow.getContext(); @@ -818,8 +858,8 @@ public class NotificationInflater { @Override protected void onPostExecute(InflationProgress result) { if (mError == null) { - mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow, - mRedactAmbient, mRemoteViewClickHandler, this); + mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags, + mCachedContentViews, mRow, mRedactAmbient, mRemoteViewClickHandler, this); } else { handleError(mError); } @@ -867,11 +907,6 @@ public class NotificationInflater { // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); } - - @Override - public boolean doInflateSynchronous() { - return mCallback != null && mCallback.doInflateSynchronous(); - } } @VisibleForTesting 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/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java index cc8adde2ed04..38df17ab52fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java @@ -112,6 +112,9 @@ public class ContextualButtonGroup extends ButtonDispatcher { * their icons for their buttons. */ public void updateIcons() { + if (getCurrentView() == null || !getCurrentView().isAttachedToWindow()) { + return; + } for (ButtonData data : mButtonData) { data.button.updateIcon(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index 5ccb9b294718..b622688a8ac6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -18,14 +18,12 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME; import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; -import static com.android.systemui.statusbar.phone.NavBarTintController.NAV_COLOR_TRANSITION_TIME_SETTING; import android.animation.ValueAnimator; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; -import android.provider.Settings; import android.util.MathUtils; import android.util.TimeUtils; @@ -167,9 +165,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, public long getTintAnimationDuration() { if (NavBarTintController.isEnabled(mContext)) { - return Math.max(Settings.Global.getInt(mContext.getContentResolver(), - NAV_COLOR_TRANSITION_TIME_SETTING, DEFAULT_COLOR_ADAPT_TRANSITION_TIME), - MIN_COLOR_ADAPT_TRANSITION_TIME); + return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME); } return DEFAULT_TINT_ANIMATION_DURATION; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java index b4f850b033e8..cf3f89ef8788 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Color; @@ -27,11 +28,13 @@ import android.os.Looper; import android.provider.Settings; import android.util.DisplayMetrics; import android.view.SurfaceControl; +import android.view.View; + +import com.android.systemui.R; public class NavBarTintController { - public static final String NAV_COLOR_TRANSITION_TIME_SETTING = "navbar_color_adapt_transition"; public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400; - public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1500; + public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700; private final HandlerThread mColorAdaptHandlerThread = new HandlerThread("ColorExtractThread"); private Handler mColorAdaptionHandler; @@ -42,23 +45,25 @@ public class NavBarTintController { // Passing the threshold of this luminance value will make the button black otherwise white private static final float LUMINANCE_THRESHOLD = 0.3f; - // The home button's icon is actually smaller than the button's size, the percentage will - // cut into the button's size to determine the icon size - private static final float PERCENTAGE_BUTTON_PADDING = 0.3f; - - // The distance from the home button to color sample around - private static final int COLOR_SAMPLE_MARGIN = 20; + // The margin from the bounds of the view to color sample around + private static final int COLOR_SAMPLE_MARGIN = 10; private boolean mRunning; private final NavigationBarView mNavigationBarView; private final LightBarTransitionsController mLightBarController; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private final int mBarRadius; + private final int mBarBottom; public NavBarTintController(NavigationBarView navigationBarView, LightBarTransitionsController lightBarController) { mNavigationBarView = navigationBarView; mLightBarController = lightBarController; + + final Resources res = navigationBarView.getResources(); + mBarRadius = res.getDimensionPixelSize(R.dimen.navigation_handle_radius); + mBarBottom = res.getDimensionPixelSize(R.dimen.navigation_handle_bottom); } public void start() { @@ -91,27 +96,28 @@ public class NavBarTintController { private void updateTint() { int[] navPos = new int[2]; int[] butPos = new int[2]; - if (mNavigationBarView.getHomeButton().getCurrentView() == null) { + View view = mNavigationBarView.getHomeHandle().getCurrentView(); + if (view == null) { return; } - // Determine the area of the home icon in the larger home button - mNavigationBarView.getHomeButton().getCurrentView().getLocationInSurface(butPos); - final int navWidth = mNavigationBarView.getHomeButton().getCurrentView().getWidth(); - final int navHeight = mNavigationBarView.getHomeButton().getCurrentView().getHeight(); - final int xPadding = (int) (PERCENTAGE_BUTTON_PADDING * navWidth); - final int yPadding = (int) (PERCENTAGE_BUTTON_PADDING * navHeight); - final Rect homeButtonRect = new Rect(butPos[0] + xPadding, butPos[1] + yPadding, - navWidth + butPos[0] - xPadding, navHeight + butPos[1] - yPadding); - if (mNavigationBarView.getCurrentView() == null || homeButtonRect.isEmpty()) { + // Determine the area of the icon within its view bounds + view.getLocationInSurface(butPos); + final int navWidth = view.getWidth(); + final int navHeight = view.getHeight(); + int viewBottom = butPos[1] + navHeight - mBarBottom; + final Rect viewIconRect = new Rect(butPos[0], viewBottom - mBarRadius * 2, + butPos[0] + navWidth, viewBottom); + + if (mNavigationBarView.getCurrentView() == null || viewIconRect.isEmpty()) { scheduleColorAdaption(); return; } mNavigationBarView.getCurrentView().getLocationOnScreen(navPos); - homeButtonRect.offset(navPos[0], navPos[1]); + viewIconRect.offset(navPos[0], navPos[1]); // Apply a margin area around the button region to sample the colors, crop from screenshot - final Rect cropRect = new Rect(homeButtonRect); + final Rect cropRect = new Rect(viewIconRect); cropRect.inset(-COLOR_SAMPLE_MARGIN, -COLOR_SAMPLE_MARGIN); if (cropRect.isEmpty()) { scheduleColorAdaption(); @@ -120,8 +126,8 @@ public class NavBarTintController { // Determine the size of the home area Rect homeArea = new Rect(COLOR_SAMPLE_MARGIN, COLOR_SAMPLE_MARGIN, - homeButtonRect.width() + COLOR_SAMPLE_MARGIN, - homeButtonRect.height() + COLOR_SAMPLE_MARGIN); + viewIconRect.width() + COLOR_SAMPLE_MARGIN, + viewIconRect.height() + COLOR_SAMPLE_MARGIN); // Get the screenshot around the home button icon to determine the color DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @@ -129,7 +135,8 @@ public class NavBarTintController { final Bitmap hardBitmap = SurfaceControl .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, mNavigationBarView.getContext().getDisplay().getRotation()); - if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()) { + if (cropRect.bottom <= hardBitmap.getHeight() + && cropRect.left + cropRect.width() <= hardBitmap.getWidth()) { final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height()); final Bitmap softBitmap = cropBitmap.copy(Config.ARGB_8888, false); @@ -204,6 +211,8 @@ public class NavBarTintController { public static boolean isEnabled(Context context) { return Settings.Global.getInt(context.getContentResolver(), - NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1; + NavigationPrototypeController.NAV_COLOR_ADAPT_ENABLE_SETTING, 0) == 1 + && Settings.Global.getInt(context.getContentResolver(), + NavigationPrototypeController.SHOW_HOME_HANDLE_SETTING, 0) == 1; } } 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/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 538d79717293..1eb499048db8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -445,8 +445,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback // Respect the disabled flag, no need for action as flag change callback will handle hiding if (rotateSuggestionsDisabled) return; - mNavigationBarView.getRotateSuggestionButton() - .onRotationProposal(rotation, winRotation, isValid); + View rotationButton = mNavigationBarView.getRotateSuggestionButton().getCurrentView(); + if (rotationButton != null && rotationButton.isAttachedToWindow()) { + mNavigationBarView.getRotateSuggestionButton() + .onRotationProposal(rotation, winRotation, isValid); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index 7c31dae2a746..c9fa89e7cb97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -65,6 +65,7 @@ public class NavigationBarInflaterView extends FrameLayout public static final String RECENT = "recent"; public static final String NAVSPACE = "space"; public static final String CLIPBOARD = "clipboard"; + public static final String HOME_HANDLE = "home_handle"; public static final String KEY = "key"; public static final String LEFT = "left"; public static final String RIGHT = "right"; @@ -393,6 +394,8 @@ public class NavigationBarInflaterView extends FrameLayout v = inflater.inflate(R.layout.clipboard, parent, false); } else if (CONTEXTUAL.equals(button)) { v = inflater.inflate(R.layout.contextual, parent, false); + } else if (HOME_HANDLE.equals(button)) { + v = inflater.inflate(R.layout.home_handle, parent, false); } else if (button.startsWith(KEY)) { String uri = extractImage(button); int code = extractKeycode(button); 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..d6d3d0807659 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION; +import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS; import android.animation.LayoutTransition; import android.animation.LayoutTransition.TransitionListener; @@ -339,6 +340,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mRightEdgePanel.setDimensions(width, height); } } + + @Override + public void onHomeHandleVisiblilityChanged(boolean visible) { + showHomeHandle(visible); + } }; public NavigationBarView(Context context, AttributeSet attrs) { @@ -376,6 +382,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); + mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); mButtonDispatchers.put(R.id.menu, menuButton); mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); @@ -573,6 +580,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav .getContextButton(R.id.rotate_suggestion); } + public ButtonDispatcher getHomeHandle() { + return mButtonDispatchers.get(R.id.home_handle); + } + public SparseArray<ButtonDispatcher> getButtonDispatchers() { return mButtonDispatchers; } @@ -855,6 +866,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final boolean showSwipeUpUI = mOverviewProxyService.shouldShowSwipeUpUI(); if (mNavigationInflaterView != null) { + if (mPrototypeController.showHomeHandle()) { + showHomeHandle(true /* visible */); + } + // Reinflate the navbar if needed, no-op unless the swipe up state changes mNavigationInflaterView.onLikelyDefaultLayoutChange(); } @@ -899,6 +914,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private void setWindowFlag(int flags, boolean enable) { final ViewGroup navbarView = ((ViewGroup) getParent()); + if (navbarView == null) { + return; + } WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView.getLayoutParams(); if (lp == null || enable == ((lp.flags & flags) != 0)) { return; @@ -912,6 +930,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav wm.updateViewLayout(navbarView, lp); } + private void showHomeHandle(boolean visible) { + mNavigationInflaterView.onTuningChanged(NAV_BAR_VIEWS, + visible ? getContext().getString(R.string.config_navBarLayoutHandle) : null); + + // Color adaption is tied with showing home handle, only avaliable if visible + if (visible) { + mColorAdaptionController.start(); + } else { + mColorAdaptionController.end(); + } + } + public void setMenuVisibility(final boolean show) { mContextualButtonGroup.setButtonVisiblity(R.id.menu, show); } @@ -1136,6 +1166,12 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav // If car mode or density changes, we need to reset the icons. updateNavButtonIcons(); } + + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + mColorAdaptionController.start(); + } else { + mColorAdaptionController.end(); + } } /** @@ -1227,10 +1263,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 +1289,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/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java new file mode 100644 index 000000000000..968074c6af59 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.animation.ArgbEvaluator; +import android.annotation.ColorInt; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.View; + +import com.android.settingslib.Utils; +import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface; + +public class NavigationHandle extends View implements ButtonInterface { + private float mDarkIntensity = -1; + + private final Paint mPaint = new Paint(); + private @ColorInt final int mLightColor; + private @ColorInt final int mDarkColor; + private final int mRadius; + private final int mBottom; + + public NavigationHandle(Context context) { + this(context, null); + } + + public NavigationHandle(Context context, AttributeSet attr) { + super(context, attr); + final Resources res = context.getResources(); + mRadius = res.getDimensionPixelSize(R.dimen.navigation_handle_radius); + mBottom = res.getDimensionPixelSize(R.dimen.navigation_handle_bottom); + + final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme); + final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme); + Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme); + Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme); + mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor); + mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); + mPaint.setAntiAlias(true); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Draw that bar + int navHeight = getHeight(); + int height = mRadius * 2; + int width = getWidth(); + int y = (navHeight - mBottom - height); + canvas.drawRoundRect(mRadius, y, width - mRadius, y + height, mRadius, mRadius, mPaint); + } + + @Override + public void setImageDrawable(Drawable drawable) { + } + + @Override + public void abortCurrentGesture() { + } + + @Override + public void setVertical(boolean vertical) { + } + + @Override + public void setDarkIntensity(float intensity) { + if (mDarkIntensity != intensity) { + mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(intensity, mLightColor, + mDarkColor)); + mDarkIntensity = intensity; + invalidate(); + } + } + + @Override + public void setDelayTouchFeedback(boolean shouldDelay) { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java index 8421e23e97b0..2c31e2cfea2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Point; import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -37,12 +38,11 @@ public class NavigationPrototypeController extends ContentObserver { private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome"; private static final String PROTOTYPE_ENABLED = "prototype_enabled"; - private static final String EDGE_SENSITIVITY_HEIGHT_SETTING = - "quickstepcontroller_edge_height_sensitivity"; public static final String EDGE_SENSITIVITY_WIDTH_SETTING = "quickstepcontroller_edge_width_sensitivity"; private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable"; + public static final String SHOW_HOME_HANDLE_SETTING = "quickstepcontroller_showhandle"; @Retention(RetentionPolicy.SOURCE) @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK, @@ -86,7 +86,7 @@ public class NavigationPrototypeController extends ContentObserver { registerObserver(GESTURE_MATCH_SETTING); registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING); registerObserver(EDGE_SENSITIVITY_WIDTH_SETTING); - registerObserver(EDGE_SENSITIVITY_HEIGHT_SETTING); + registerObserver(SHOW_HOME_HANDLE_SETTING); } /** @@ -114,20 +114,29 @@ public class NavigationPrototypeController extends ContentObserver { } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) { mListener.onColorAdaptChanged( NavBarTintController.isEnabled(mContext)); - } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING) - || path.endsWith(EDGE_SENSITIVITY_HEIGHT_SETTING)) { + } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING)) { mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(), getEdgeSensitivityHeight()); + } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) { + mListener.onHomeHandleVisiblilityChanged(showHomeHandle()); } } } + /** + * @return the width for edge swipe + */ public int getEdgeSensitivityWidth() { return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_WIDTH_SETTING, 0)); } + /** + * @return full screen height + */ public int getEdgeSensitivityHeight() { - return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_HEIGHT_SETTING, 0)); + final Point size = new Point(); + mContext.getDisplay().getRealSize(size); + return size.y; } public boolean isEnabled() { @@ -149,6 +158,10 @@ public class NavigationPrototypeController extends ContentObserver { return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */); } + boolean showHomeHandle() { + return getGlobalBool(SHOW_HOME_HANDLE_SETTING, false /* default */); + } + /** * Since Settings.Global cannot pass arrays, use a string to represent each character as a * gesture map to actions corresponding to {@see GestureAction}. The number is represented as: @@ -185,6 +198,7 @@ public class NavigationPrototypeController extends ContentObserver { void onGestureRemap(@GestureAction int[] actions); void onBackButtonVisibilityChanged(boolean visible); void onHomeButtonVisibilityChanged(boolean visible); + void onHomeHandleVisiblilityChanged(boolean visible); void onColorAdaptChanged(boolean enabled); void onEdgeSensitivityChanged(int width, int height); } 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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 069703e9dd81..f4fa1e8246e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -590,11 +590,6 @@ public class NotificationPanelView extends PanelView implements mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); - // Move big clock up while pulling up the bouncer - PropertyAnimator.setProperty(mBigClockContainer, AnimatableProperty.Y, - MathUtils.lerp(-mBigClockContainer.getHeight(), 0, - Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(getExpandedFraction())), - CLOCK_ANIMATION_PROPERTIES, animateClock); updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPadding; } @@ -1334,8 +1329,7 @@ public class NotificationPanelView extends PanelView implements } }; - private void setKeyguardBottomAreaVisibility(int statusBarState, - boolean goingToFullShade) { + private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { mKeyguardBottomArea.animate().cancel(); if (goingToFullShade) { mKeyguardBottomArea.animate() @@ -1438,6 +1432,7 @@ public class NotificationPanelView extends PanelView implements if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) { updateKeyguardBottomAreaAlpha(); + updateBigClockAlpha(); } if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling && mQsScrimEnabled) { @@ -1883,6 +1878,19 @@ public class NotificationPanelView extends PanelView implements } } + /** + * Custom clock fades away when user drags up to unlock or pulls down quick settings. + * + * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See + * {@link updateKeyguardBottomAreaAlpha}. + */ + private void updateBigClockAlpha() { + float expansionAlpha = MathUtils.map(isUnlockHintRunning() + ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction()); + float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); + mBigClockContainer.setAlpha(alpha); + } + private float getNotificationsTopY() { if (mNotificationStackScroller.getNotGoneChildCount() == 0) { return getExpandedHeight(); @@ -2597,6 +2605,7 @@ public class NotificationPanelView extends PanelView implements } mNotificationStackScroller.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); + updateBigClockAlpha(); updateStatusBarIcons(); } 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 6b6998d7c4a0..51ffc1dd98cc 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(); } @@ -3267,7 +3261,11 @@ public class StatusBar extends SystemUI implements DemoMode, return true; } if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { - animateCollapsePanels(); + if (mNotificationPanel.canPanelBeCollapsed()) { + animateCollapsePanels(); + } else { + mBubbleController.performBackPressIfNeeded(); + } return true; } if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) { @@ -4004,8 +4002,7 @@ public class StatusBar extends SystemUI implements DemoMode, float viewY = screenY - mTmpInt2[1]; if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { - if (mAmbientIndicationContainer instanceof DozeReceiver) - ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap(); + dispatchTap(mAmbientIndicationContainer, viewX, viewY); } } } @@ -4020,6 +4017,12 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setAodFrontScrimAlpha(scrimOpacity); } + private void dispatchTap(View view, float x, float y) { + long now = SystemClock.elapsedRealtime(); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); + } + private void dispatchTouchEvent(View view, float x, float y, long now, int action) { MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); view.dispatchTouchEvent(ev); 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/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java index 77895c97051c..190ce7550511 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java @@ -75,6 +75,17 @@ public class KeyguardSliceViewTest extends SysuiTestCase { } @Test + public void hasHeader_readsSliceData() { + ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY); + mKeyguardSliceView.onChanged(builder.build()); + Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader()); + + builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!")); + mKeyguardSliceView.onChanged(builder.build()); + Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader()); + } + + @Test public void refresh_replacesSliceContentAndNotifiesListener() { AtomicBoolean notified = new AtomicBoolean(); mKeyguardSliceView.setContentChangeListener(()-> notified.set(true)); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java new file mode 100644 index 000000000000..58701e72e406 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -0,0 +1,163 @@ +/* + * 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.keyguard.clock; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.LayoutInflater; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.util.InjectionInflationController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public final class ClockManagerTest extends SysuiTestCase { + + private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); + private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; + + private ClockManager mClockManager; + private ContentObserver mContentObserver; + private DockManagerFake mFakeDockManager; + @Mock InjectionInflationController mMockInjectionInflationController; + @Mock SysuiColorExtractor mMockColorExtractor; + @Mock ContentResolver mMockContentResolver; + @Mock SettingsWrapper mMockSettingsWrapper; + @Mock ClockManager.ClockChangedListener mMockListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + LayoutInflater inflater = LayoutInflater.from(getContext()); + when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater); + + mFakeDockManager = new DockManagerFake(); + mClockManager = new ClockManager(getContext(), mMockInjectionInflationController, + mFakeDockManager, mMockColorExtractor, mMockContentResolver, mMockSettingsWrapper); + + mClockManager.addOnClockChangedListener(mMockListener); + mContentObserver = mClockManager.getContentObserver(); + } + + @After + public void tearDown() { + mClockManager.removeOnClockChangedListener(mMockListener); + } + + @Test + public void dockEvent() { + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + assertThat(mClockManager.isDocked()).isTrue(); + } + + @Test + public void undockEvent() { + mFakeDockManager.setDockEvent(DockManager.STATE_NONE); + assertThat(mClockManager.isDocked()).isFalse(); + } + + @Test + public void getCurrentClock_default() { + // GIVEN that settings doesn't contain any values + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null); + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null); + // WHEN settings change event is fired + mContentObserver.onChange(false); + // THEN the result is null, indicated the default clock face should be used. + assertThat(mClockManager.getCurrentClock()).isNull(); + } + + @Test + public void getCurrentClock_customClock() { + // GIVEN that settings is set to the bubble clock face + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN settings change event is fired + mContentObserver.onChange(false); + // THEN the plugin is the bubble clock face. + assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); + } + + @Test + public void getCurrentClock_badSettingsValue() { + // GIVEN that settings contains a value that doesn't correspond to a + // custom clock face. + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value"); + // WHEN settings change event is fired + mContentObserver.onChange(false); + // THEN the result is null. + assertThat(mClockManager.getCurrentClock()).isNull(); + } + + @Test + public void getCurrentClock_dockedDefault() { + // WHEN dock event is fired + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + // THEN the result is null, indicating the default clock face. + assertThat(mClockManager.getCurrentClock()).isNull(); + } + + @Test + public void getCurrentClock_dockedCustomClock() { + // GIVEN settings is set to the bubble clock face + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN dock event fires + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + // THEN the plugin is the bubble clock face. + assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); + } + + @Test + public void getCurrentClock_badDockedSettingsValue() { + // GIVEN settings contains a value that doesn't correspond to an available clock face. + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + // WHEN dock event fires + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + // THEN the result is null. + assertThat(mClockManager.getCurrentClock()).isNull(); + } + + @Test + public void getCurrentClock_badDockedSettingsFallback() { + // GIVEN settings contains a value that doesn't correspond to an available clock face, but + // locked screen settings is set to bubble clock. + when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + // WHEN dock event is fired + mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); + // THEN the plugin is the bubble clock face. + assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 2742577db860..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); @@ -207,12 +211,12 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow2.getEntry().showInShadeWhenBubble()); // Switch which bubble is expanded stackView.setExpandedBubble(mRow.getEntry()); - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow.getEntry().showInShadeWhenBubble()); // collapse for previous bubble @@ -262,19 +266,19 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow2.getEntry().showInShadeWhenBubble()); // Dismiss currently expanded - mBubbleController.removeBubble(stackView.getExpandedBubble().getKey()); + mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey()); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); // Make sure next bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry()); + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); // Dismiss that one - mBubbleController.removeBubble(stackView.getExpandedBubble().getKey()); + mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey()); // Make sure state changes and collapse happens verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); @@ -297,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 98bf3c2743a8..bb384dd52875 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager +import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.os.Handler @@ -28,11 +29,18 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import com.android.systemui.Dependency +import com.android.systemui.Dependency.BG_HANDLER +import com.android.systemui.Dependency.MAIN_HANDLER 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 @@ -81,15 +89,20 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var testableLooper: TestableLooper private lateinit var privacyItemController: PrivacyItemController + private lateinit var handler: Handler + + fun PrivacyItemController(context: Context) = + PrivacyItemController(context, appOpsController, handler, handler) @Before fun setup() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) + handler = Handler(testableLooper.looper) appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper) - mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper)) + mDependency.injectTestDependency(Dependency.BG_HANDLER, handler) + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler) mContext.addMockSystemService(UserManager::class.java, userManager) mContext.getOrCreateTestableResources().addOverride(R.string.device_services, DEVICE_SERVICES_STRING) @@ -232,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..99dc9f87520f 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) { } }); } @@ -98,7 +99,11 @@ public class NotificationInflaterTest extends SysuiTestCase { public void testIncreasedHeadsUpBeingUsed() { mNotificationInflater.setUsesIncreasedHeadsUpHeight(true); Notification.Builder builder = spy(mBuilder); - mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext); + mNotificationInflater.inflateNotificationViews( + false /* inflateSynchronously */, + FLAG_CONTENT_VIEW_ALL, + builder, + mContext); verify(builder).createHeadsUpContentView(true); } @@ -106,7 +111,11 @@ public class NotificationInflaterTest extends SysuiTestCase { public void testIncreasedHeightBeingUsed() { mNotificationInflater.setUsesIncreasedHeight(true); Notification.Builder builder = spy(mBuilder); - mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext); + mNotificationInflater.inflateNotificationViews( + false /* inflateSynchronously */, + FLAG_CONTENT_VIEW_ALL, + builder, + mContext); verify(builder).createContentView(true); } @@ -158,14 +167,18 @@ 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( + false /* inflateSynchronously */, + 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 +188,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 +212,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 +230,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 +246,20 @@ 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.setInflateSynchronously(true); + inflater.setInflationCallback(new InflationCallback() { @Override public void handleInflationException(StatusBarNotification notification, Exception e) { @@ -257,18 +271,13 @@ 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")); } countDownLatch.countDown(); } - - @Override - public boolean doInflateSynchronous() { - return true; - } }); block.run(); assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)); 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/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java index 6177344a3ef4..5c8071885c0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java @@ -24,9 +24,11 @@ import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.ImageReader; +import android.os.SystemClock; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.util.Log; import android.view.Display; import android.view.DisplayInfo; @@ -39,12 +41,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.function.Predicate; + /** atest NavigationBarButtonTest */ @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest public class NavigationBarButtonTest extends SysuiTestCase { + private static final String TAG = "NavigationBarButtonTest"; private ImageReader mReader; private NavigationBarView mNavBar; private VirtualDisplay mVirtualDisplay; @@ -78,9 +83,31 @@ public class NavigationBarButtonTest extends SysuiTestCase { assertNotNull("virtual display must not be null", mVirtualDisplay); + waitForDisplayReady(mVirtualDisplay.getDisplay().getDisplayId()); + return mVirtualDisplay.getDisplay(); } + private void waitForDisplayReady(int displayId) { + waitForDisplayCondition(displayId, state -> state); + } + + private void waitForDisplayCondition(int displayId, Predicate<Boolean> condition) { + for (int retry = 1; retry <= 10; retry++) { + if (condition.test(isDisplayOn(displayId))) { + return; + } + Log.i(TAG, "Waiting for virtual display ready ... retry = " + retry); + SystemClock.sleep(500); + } + } + + private boolean isDisplayOn(int displayId) { + DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + Display display = displayManager.getDisplay(displayId); + return display != null && display.getState() == Display.STATE_ON; + } + @After public void tearDown() { releaseDisplay(); 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/packages/VpnDialogs/Android.bp b/packages/VpnDialogs/Android.bp new file mode 100644 index 000000000000..6f2f50c0ddd4 --- /dev/null +++ b/packages/VpnDialogs/Android.bp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "VpnDialogs", + certificate: "platform", + privileged: true, + srcs: ["src/**/*.java"], + platform_apis: true, +} diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk deleted file mode 100644 index 850764645644..000000000000 --- a/packages/VpnDialogs/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_CERTIFICATE := platform - -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := VpnDialogs -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp new file mode 100644 index 000000000000..1bec49234b6a --- /dev/null +++ b/packages/WAPPushManager/Android.bp @@ -0,0 +1,12 @@ +// Copyright 2007-2008 The Android Open Source Project + +android_app { + name: "WAPPushManager", + srcs: ["src/**/*.java"], + platform_apis: true, + libs: ["telephony-common"], + static_libs: ["android-common"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, +} diff --git a/packages/WAPPushManager/Android.mk b/packages/WAPPushManager/Android.mk deleted file mode 100644 index 91526dd19ce0..000000000000 --- a/packages/WAPPushManager/Android.mk +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2007-2008 The Android Open Source Project - - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := WAPPushManager -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_JAVA_LIBRARIES += telephony-common -LOCAL_STATIC_JAVA_LIBRARIES += android-common - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -include $(BUILD_PACKAGE) - -# This finds and builds the test apk as well, so a single make does both. -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/WAPPushManager/tests/Android.bp b/packages/WAPPushManager/tests/Android.bp new file mode 100644 index 000000000000..25c6121324ea --- /dev/null +++ b/packages/WAPPushManager/tests/Android.bp @@ -0,0 +1,34 @@ +// Copyright 2008, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "WAPPushManagerTests", + libs: [ + "android.test.runner", + "telephony-common", + "android.test.base", + ], + static_libs: ["junit"], + // Include all test java files. + srcs: [ + "src/**/*.java", + "src/com/android/smspush/unitTests/IDataVerify.aidl", + ], + // Notice that we don't have to include the src files of Email + // because running the tests using an instrumentation targeting + // Email, we automatically get all of its classes loaded into + // our environment. + platform_apis: true, + instrumentation_for: "WAPPushManager", +} diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk deleted file mode 100644 index c4c2240f102c..000000000000 --- a/packages/WAPPushManager/tests/Android.mk +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2008, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base -LOCAL_STATIC_JAVA_LIBRARIES := junit - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_SRC_FILES += \ - src/com/android/smspush/unitTests/IDataVerify.aidl - - -# Notice that we don't have to include the src files of Email because, by -# running the tests using an instrumentation targeting Eamil, we -# automatically get all of its classes loaded into our environment. - -LOCAL_PACKAGE_NAME := WAPPushManagerTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_INSTRUMENTATION_FOR := WAPPushManager - -include $(BUILD_PACKAGE) - diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp new file mode 100644 index 000000000000..56020cd573b1 --- /dev/null +++ b/packages/WallpaperBackup/Android.bp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "WallpaperBackup", + srcs: ["src/**/*.java"], + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + platform_apis: true, + certificate: "platform", + privileged: false, +} diff --git a/packages/WallpaperBackup/Android.mk b/packages/WallpaperBackup/Android.mk deleted file mode 100644 index a6426a6ce217..000000000000 --- a/packages/WallpaperBackup/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_PACKAGE_NAME := WallpaperBackup -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := false - -include $(BUILD_PACKAGE) - -######################## -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 4254a0ba200a..2e40c1acfa2c 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -115,8 +115,10 @@ public class WallpaperBackupAgent extends BackupAgent { // We always back up this 'empty' file to ensure that the absence of // storable wallpaper imagery still produces a non-empty backup data // stream, otherwise it'd simply be ignored in preflight. - FileOutputStream touch = new FileOutputStream(empty); - touch.close(); + if (!empty.exists()) { + FileOutputStream touch = new FileOutputStream(empty); + touch.close(); + } fullBackupFile(empty, data); SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); diff --git a/packages/WallpaperCropper/Android.bp b/packages/WallpaperCropper/Android.bp new file mode 100644 index 000000000000..40c423520081 --- /dev/null +++ b/packages/WallpaperCropper/Android.bp @@ -0,0 +1,11 @@ +android_app { + name: "WallpaperCropper", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", + product_specific: true, + privileged: true, + optimize: { + proguard_flags_files: ["proguard.flags"], + }, +} diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk deleted file mode 100644 index 2fa1ddee0fd9..000000000000 --- a/packages/WallpaperCropper/Android.mk +++ /dev/null @@ -1,18 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := WallpaperCropper -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRODUCT_MODULE := true -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index 1294dbbbfa87..7ce13c2f59e9 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -44,6 +44,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := frameworks-base-overlays-debug LOCAL_REQUIRED_MODULES := \ ExperimentNavigationBarFloatingOverlay \ + ExperimentNavigationBarVisualInsetOverlay \ ExperimentNavigationBarDefaultOverlay \ ExperimentNavigationBarSlimOverlay32 \ ExperimentNavigationBarSlimOverlay40 \ diff --git a/tests/HwAccelerationTest/Android.mk b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/Android.mk index 79072faeaa0e..56bf51642c9d 100644 --- a/tests/HwAccelerationTest/Android.mk +++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/Android.mk @@ -1,11 +1,11 @@ # -# Copyright (C) 2010 The Android Open Source Project +# Copyright 2018, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -17,12 +17,14 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +LOCAL_RRO_THEME := ExperimentNavigationBarVisualInset +LOCAL_CERTIFICATE := platform + LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := HwAccelerationTest -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := ExperimentNavigationBarVisualInsetOverlay +LOCAL_SDK_VERSION := current -include $(BUILD_PACKAGE) +include $(BUILD_RRO_PACKAGE)
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..3ea5dc4716ff --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/AndroidManifest.xml @@ -0,0 +1,27 @@ +<!-- +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.internal.experiment.navbar.type.inset" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.experiment_navbar_visual_inset" + android:priority="1"/> + + <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/> +</manifest>
\ No newline at end of file diff --git a/core/java/android/app/IInputForwarder.aidl b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/config.xml index d6be63eb54e5..d35a744e4e59 100644 --- a/core/java/android/app/IInputForwarder.aidl +++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/config.xml @@ -1,5 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- /** - * Copyright (c) 2017, The Android Open Source Project + * Copyright (c) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +15,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package android.app; - -import android.view.InputEvent; - -/** - * Forwards input events into owned activity container, used in {@link android.app.ActivityView}. - * To forward input to other apps {@link android.Manifest.permission.INJECT_EVENTS} permission is - * required. - * @hide - */ -interface IInputForwarder { - boolean forwardEvent(in InputEvent event); -}
\ No newline at end of file +--> +<resources> + <!-- Height of the bottom navigation / system bar. --> + <dimen name="navigation_bar_height">16dp</dimen> + <!-- Width of the navigation bar when it is placed vertically on the screen --> + <dimen name="navigation_bar_width">16dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml new file mode 100644 index 000000000000..84dfcb76706b --- /dev/null +++ b/packages/overlays/ExperimentNavigationBarVisualInsetOverlay/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Name of overlay [CHAR LIMIT=64] --> + <string name="experiment_navigationbar_overlay" translatable="false">Visual Inset Navigation Bar</string> +</resources>
\ No newline at end of file diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp new file mode 100644 index 000000000000..93b2d956cf6f --- /dev/null +++ b/packages/services/PacProcessor/Android.bp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "PacProcessor", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", + jni_libs: ["libjni_pacprocessor"], +} diff --git a/packages/services/PacProcessor/Android.mk b/packages/services/PacProcessor/Android.mk deleted file mode 100644 index be9ba4351056..000000000000 --- a/packages/services/PacProcessor/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := PacProcessor -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -LOCAL_JNI_SHARED_LIBRARIES := libjni_pacprocessor - -include $(BUILD_PACKAGE) diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp new file mode 100644 index 000000000000..87aa7637df8e --- /dev/null +++ b/packages/services/Proxy/Android.bp @@ -0,0 +1,7 @@ +android_app { + name: "ProxyHandler", + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "platform", + privileged: true, +} diff --git a/packages/services/Proxy/Android.mk b/packages/services/Proxy/Android.mk deleted file mode 100644 index ce1715f08be5..000000000000 --- a/packages/services/Proxy/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := ProxyHandler -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 9b863a9f2d26..61f63d3bbc3d 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -47,7 +47,7 @@ final class RemoteAugmentedAutofillService private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, @@ -106,6 +106,12 @@ final class RemoteAugmentedAutofillService activityComponent, focusedId, focusedValue)); } + @Override + public String toString() { + return "RemoteAugmentedAutofillService[" + + ComponentName.flattenToShortString(getComponentName()) + "]"; + } + /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ @@ -181,11 +187,13 @@ final class RemoteAugmentedAutofillService @Override protected void onTimeout(RemoteAugmentedAutofillService remoteService) { - Slog.wtf(TAG, "timed out: " + this); + // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the + // timeout + Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS + + "ms) for " + remoteService); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks finish(); } - } public interface RemoteAugmentedAutofillServiceCallbacks 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..32e2cacbb37b 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -70,6 +70,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -130,6 +131,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; @@ -1483,19 +1485,50 @@ public class UserBackupManagerService { } /** - * Clear an application's data, blocking until the operation completes or times out. If {@code - * keepSystemState} is {@code true}, we intentionally do not clear system state that would - * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're - * bringing it into the actual expected state related to the already-restored notification state - * etc. + * Clear an application's data after a failed restore, blocking until the operation completes or + * times out. */ - public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) { - // Don't wipe packages marked allowClearUserData=false + public void clearApplicationDataAfterRestoreFailure(String packageName) { + clearApplicationDataSynchronous(packageName, true, false); + } + + /** + * Clear an application's data before restore, blocking until the operation completes or times + * out. + */ + public void clearApplicationDataBeforeRestore(String packageName) { + clearApplicationDataSynchronous(packageName, false, true); + } + + /** + * Clear an application's data, blocking until the operation completes or times out. + * + * @param checkFlagAllowClearUserDataOnFailedRestore if {@code true} uses + * {@link ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE} to decide if + * clearing data is allowed after a failed restore. + * + * @param keepSystemState if {@code true}, we don't clear system state such as already restored + * notification settings, permission grants, etc. + */ + private void clearApplicationDataSynchronous(String packageName, + boolean checkFlagAllowClearUserDataOnFailedRestore, boolean keepSystemState) { try { - PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { + ApplicationInfo applicationInfo = mPackageManager.getPackageInfoAsUser( + packageName, 0, mUserId).applicationInfo; + + boolean shouldClearData; + if (checkFlagAllowClearUserDataOnFailedRestore + && applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) { + shouldClearData = (applicationInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) != 0; + } else { + shouldClearData = + (applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) != 0; + } + + if (!shouldClearData) { if (MORE_DEBUG) { - Slog.i(TAG, "allowClearUserData=false so not wiping " + Slog.i(TAG, "Clearing app data is not allowed so not wiping " + packageName); } return; @@ -1510,8 +1543,8 @@ public class UserBackupManagerService { synchronized (mClearDataLock) { mClearingData = true; try { - mActivityManager.clearApplicationUserData( - packageName, keepSystemState, observer, mUserId); + mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer, + mUserId); } catch (RemoteException e) { // can't happen because the activity manager is in this process } @@ -2319,6 +2352,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 +2365,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 +2379,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/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index c5389fa5f878..836a5e883c62 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -352,7 +352,7 @@ public class FullRestoreEngine extends RestoreEngine { Slog.d(TAG, "Clearing app data preparatory to full restore"); } - mBackupManagerService.clearApplicationDataSynchronous(pkg, true); + mBackupManagerService.clearApplicationDataBeforeRestore(pkg); } else { if (MORE_DEBUG) { Slog.d(TAG, "backup agent (" diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 324c2d974010..6714b0aea261 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -660,7 +660,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ++mCount; } catch (Exception e) { Slog.e(TAG, "Error when attempting restore: " + e.toString()); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(false); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } } @@ -686,6 +686,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { boolean staging = !packageName.equals("android"); ParcelFileDescriptor stage; File downloadFile = (staging) ? mStageName : mBackupDataName; + boolean startedAgentRestore = false; try { IBackupTransport transport = @@ -766,13 +767,15 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); backupManagerService.prepareOperationTimeout( mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT); + startedAgentRestore = true; mAgent.doRestore(mBackupData, appVersionCode, mNewState, mEphemeralOpToken, backupManagerService.getBackupManagerBinder()); } catch (Exception e) { Slog.e(TAG, "Unable to call app for restore: " + packageName, e); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); - keyValueAgentErrorCleanup(); // clears any pending timeout messages as well + // Clears any pending timeout messages as well. + keyValueAgentErrorCleanup(startedAgentRestore); // After a restore failure we go back to running the queue. If there // are no more packages to be restored that will be handled by the @@ -832,7 +835,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { Slog.e(TAG, "Unable to finalize restore of " + packageName); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } } @@ -988,8 +991,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // We also need to wipe the current target's data, as it's probably // in an incoherent state. - backupManagerService.clearApplicationDataSynchronous( - mCurrentPackage.packageName, false); + backupManagerService.clearApplicationDataAfterRestoreFailure( + mCurrentPackage.packageName); // Schedule the next state based on the nature of our failure if (status == BackupTransport.TRANSPORT_ERROR) { @@ -1110,11 +1113,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mListener.onFinished(callerLogString); } - void keyValueAgentErrorCleanup() { - // If the agent fails restore, it might have put the app's data - // into an incoherent state. For consistency we wipe its data - // again in this case before continuing with normal teardown - backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName, false); + /** + * @param clearAppData - set to {@code true} if the backup agent had already been invoked when + * restore faied. So the app data may be in corrupted state and has to be cleared. + */ + void keyValueAgentErrorCleanup(boolean clearAppData) { + if (clearAppData) { + // If the agent fails restore, it might have put the app's data + // into an incoherent state. For consistency we wipe its data + // again in this case before continuing with normal teardown + backupManagerService.clearApplicationDataAfterRestoreFailure( + mCurrentPackage.packageName); + } keyValueAgentCleanup(); } @@ -1251,7 +1261,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Some kind of horrible semantic error; we're in an unexpected state. // Back off hard and wind up. Slog.e(TAG, "Unexpected restore callback into state " + mState); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); nextState = UnifiedRestoreState.FINAL; break; } @@ -1271,7 +1281,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName, "restore timeout"); // Handle like an agent that threw on invocation: wipe it and go on to the next - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } 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..45ceeda689f7 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) { @@ -480,31 +505,6 @@ public final class ContentCaptureManagerService extends } @Override - public void setContentCaptureFeatureEnabled(boolean enabled, - @NonNull IResultReceiver result) { - final int userId = UserHandle.getCallingUserId(); - final boolean isService; - synchronized (mLock) { - isService = assertCalledByServiceLocked("setContentCaptureFeatureEnabled()", userId, - Binder.getCallingUid(), result); - } - if (!isService) return; - - final long token = Binder.clearCallingIdentity(); - try { - Settings.Secure.putStringForUser(getContext().getContentResolver(), - Settings.Secure.CONTENT_CAPTURE_ENABLED, Boolean.toString(enabled), userId); - } finally { - Binder.restoreCallingIdentity(token); - } - try { - result.send(ContentCaptureManager.RESULT_CODE_TRUE, /* resultData= */null); - } catch (RemoteException e) { - Slog.w(mTag, "Unable to send setContentCaptureFeatureEnabled(): " + e); - } - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return; @@ -527,9 +527,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..9e159600e42c 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; @@ -43,11 +44,12 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; 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 +89,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 +193,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 +224,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 +267,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 +418,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,11 +460,26 @@ 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 } + + @Override + public void disableSelf() { + if (mMaster.verbose) Slog.v(TAG, "disableSelf()"); + + final long token = Binder.clearCallingIdentity(); + try { + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.CONTENT_CAPTURE_ENABLED, "false", mUserId); + } finally { + Binder.restoreCallingIdentity(token); + } + } } } 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 ebd4c5584fe1..775e4c8cf4ed 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -49,7 +49,7 @@ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = - "This probably mean the selinux policies need to be changed."; + "This probably means the selinux policies need to be changed."; private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @@ -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(); @@ -159,6 +162,15 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); - return extconDir.exists() && extconDir.isDirectory(); + boolean retVal = extconDir.exists() && extconDir.isDirectory(); + // TODO(b/124364409): return the correct value after selinux policy is updated. + if (retVal) { + Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir " + + extconDir.isDirectory() + + " but reporting it does not exist until selinux policies are updated." + + " see b/124364409" + ); + } + return false; } } 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 3479e18b97c5..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) { @@ -2069,7 +2039,7 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!provider.isUseableLocked()) { if (isSettingsExemptLocked(record)) { - providerRequest.forceLocation = true; + providerRequest.locationSettingsIgnored = true; providerRequest.lowPowerMode = false; } else { continue; @@ -2079,8 +2049,9 @@ public class LocationManagerService extends ILocationManager.Stub { LocationRequest locationRequest = record.mRealRequest; long interval = locationRequest.getInterval(); + // if we're forcing location, don't apply any throttling - if (!providerRequest.forceLocation && !isThrottlingExemptLocked( + if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked( record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); @@ -2165,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) { @@ -2710,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 { @@ -2789,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 { @@ -2816,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; } } @@ -2830,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; } } @@ -2863,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/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 62da3f8e01ba..6e5d31640e4f 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.util.StatsLog; import com.android.internal.util.ArrayUtils; +import com.android.server.utils.FlagNamespaceUtils; import java.io.File; @@ -194,6 +195,8 @@ public class RescueParty { RecoverySystem.rebootPromptAndWipeUserData(context, TAG); break; } + FlagNamespaceUtils.addToKnownResetNamespaces( + FlagNamespaceUtils.NAMESPACE_NO_PACKAGE); } private static void resetAllSettings(Context context, int mode) throws Exception { @@ -203,14 +206,19 @@ public class RescueParty { final ContentResolver resolver = context.getContentResolver(); try { Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM); - } catch (Throwable t) { - res = new RuntimeException("Failed to reset global settings", t); + } catch (Exception e) { + res = new RuntimeException("Failed to reset global settings", e); + } + try { + FlagNamespaceUtils.resetDeviceConfig(mode); + } catch (Exception e) { + res = new RuntimeException("Failed to reset config settings", e); } for (int userId : getAllUserIds()) { try { Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); - } catch (Throwable t) { - res = new RuntimeException("Failed to reset secure settings for " + userId, t); + } catch (Exception e) { + res = new RuntimeException("Failed to reset secure settings for " + userId, e); } } if (res != null) { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 2f1510e32311..fd946cdfa48a 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -236,7 +237,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PhoneCapability mPhoneCapability = null; - private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @TelephonyManager.RadioPowerState private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE; @@ -246,14 +247,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PreciseDataConnectionState mPreciseDataConnectionState = new PreciseDataConnectionState(); - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = + // Nothing here yet, but putting it here in case we want to add more in the future. + static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0; + + static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO; static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR - | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; + | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST + | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | @@ -637,8 +642,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false)); + } else { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true)); + } } catch (RemoteException ex) { remove(r.binder); } @@ -673,7 +684,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged( new Bundle(mCellLocation[phoneId])); } @@ -722,7 +733,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -809,9 +820,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) { + if ((events & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE) != 0) { try { - r.callback.onPreferredDataSubIdChanged(mPreferredDataSubId); + r.callback.onActiveDataSubIdChanged(mActiveDataSubId); } catch (RemoteException ex) { remove(r.binder); } @@ -1009,13 +1020,22 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && idMatch(r.subId, subId, phoneId)) { + try { + ServiceState stateToSend; + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = new ServiceState(state); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = state.sanitizeLocationInfo(false); + } else { + stateToSend = state.sanitizeLocationInfo(true); + } if (DBG) { log("notifyServiceStateForSubscriber: callback.onSSC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - r.callback.onServiceStateChanged(new ServiceState(state)); + r.callback.onServiceStateChanged(stateToSend); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1198,7 +1218,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); @@ -1500,7 +1520,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -1738,23 +1758,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyPreferredDataSubIdChanged(int preferredSubId) { - if (!checkNotifyPermission("notifyPreferredDataSubIdChanged()")) { + public void notifyActiveDataSubIdChanged(int activeDataSubId) { + if (!checkNotifyPermission("notifyActiveDataSubIdChanged()")) { return; } if (VDBG) { - log("notifyPreferredDataSubIdChanged: preferredSubId=" + preferredSubId); + log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId); } synchronized (mRecords) { - mPreferredDataSubId = preferredSubId; + mActiveDataSubId = activeDataSubId; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE)) { + PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE)) { try { - r.callback.onPreferredDataSubIdChanged(preferredSubId); + r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1890,7 +1910,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mBackgroundCallState=" + mBackgroundCallState); pw.println("mSrvccState=" + mSrvccState); pw.println("mPhoneCapability=" + mPhoneCapability); - pw.println("mPreferredDataSubId=" + mPreferredDataSubId); + pw.println("mActiveDataSubId=" + mActiveDataSubId); pw.println("mRadioPowerState=" + mRadioPowerState); pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mCallQuality=" + mCallQuality); @@ -2108,12 +2128,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean checkListenerPermission( int events, int subId, String callingPackage, String message) { + LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); + + boolean shouldCheckLocationPermissions = false; if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(), - callingPackage) != AppOpsManager.MODE_ALLOWED) { - return false; + locationQueryBuilder.setMinSdkVersionForCoarse(0); + shouldCheckLocationPermissions = true; + } + + if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + // Everything that requires fine location started in Q. So far... + locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); + shouldCheckLocationPermissions = true; + } + + if (shouldCheckLocationPermissions) { + LocationAccessPolicy.LocationPermissionResult result = + LocationAccessPolicy.checkLocationPermission( + mContext, locationQueryBuilder.build()); + switch (result) { + case DENIED_HARD: + throw new SecurityException("Unable to listen for events " + events + " due to " + + "insufficient location permissions."); + case DENIED_SOFT: + return false; } } @@ -2139,14 +2182,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) { - // It can have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE. - TelephonyPermissions.checkReadPhoneState(mContext, - SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(), - Binder.getCallingUid(), callingPackage, "listen to " - + "LISTEN_PREFERRED_DATA_SUBID_CHANGE"); - } - if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); @@ -2228,15 +2263,38 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - private boolean checkLocationAccess(Record r) { - long token = Binder.clearCallingIdentity(); - try { - return LocationAccessPolicy.canAccessCellLocation(mContext, - r.callingPackage, r.callerUid, r.callerPid, - /*throwOnDeniedPermission*/ false); - } finally { - Binder.restoreCallingIdentity(token); - } + private boolean checkFineLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForFine(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); + } + + private boolean checkCoarseLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForCoarse(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); } private void checkPossibleMissNotify(Record r, int phoneId) { @@ -2286,7 +2344,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2336,7 +2394,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); } } catch (RemoteException ex) { 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 026430b5469d..aebe2b78a853 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -319,10 +319,6 @@ public final class ActiveServices { ServiceRecord r = mDelayedStartList.remove(0); if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (exec next): " + r); - if (r.pendingStarts.size() <= 0) { - Slog.w(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested - + " delayedStop=" + r.delayedStop); - } if (DEBUG_DELAYED_SERVICE) { if (mDelayedStartList.size() > 0) { Slog.v(TAG_SERVICE, "Remaining delayed list:"); @@ -332,11 +328,16 @@ public final class ActiveServices { } } r.delayed = false; - try { - startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true, - false); - } catch (TransactionTooLargeException e) { - // Ignore, nobody upstack cares. + if (r.pendingStarts.size() <= 0) { + Slog.wtf(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested + + " delayedStop=" + r.delayedStop); + } else { + try { + startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true, + false); + } catch (TransactionTooLargeException e) { + // Ignore, nobody upstack cares. + } } } if (mStartingBackground.size() > 0) { @@ -2978,6 +2979,7 @@ public final class ActiveServices { // Clear start entries. r.clearDeliveredStartsLocked(); r.pendingStarts.clear(); + smap.mDelayedStartList.remove(r); if (r.app != null) { synchronized (r.stats.getBatteryStats()) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c4a9db6b7262..98f2c9f21f3a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2704,8 +2704,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void batterySendBroadcast(Intent intent) { synchronized (this) { broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, - -1, SYSTEM_UID, UserHandle.USER_ALL); + OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -3823,12 +3823,13 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); if (isInstantApp) { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0, - null, null, permission.ACCESS_INSTANT_APPS, null, false, false, - resolvedUserId, false); + broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null, + null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false, + false, resolvedUserId, false); } else { - broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0, - null, null, null, null, false, false, resolvedUserId, false); + broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null, + null, 0, null, null, null, null, false, false, resolvedUserId, + false); } if (observer != null) { @@ -4263,7 +4264,8 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid)); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.getUserId(uid)); } private void cleanupDisabledPackageComponentsLocked( @@ -8709,6 +8711,8 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.showSystemReadyErrorDialogsIfNeeded(); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); long ident = Binder.clearCallingIdentity(); try { Intent intent = new Intent(Intent.ACTION_USER_STARTED); @@ -8717,7 +8721,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -8731,7 +8735,8 @@ public class ActivityManagerService extends IActivityManager.Stub } }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + UserHandle.USER_ALL); } catch (Throwable t) { Slog.wtf(TAG, "Failed sending first user broadcasts", t); } finally { @@ -14369,10 +14374,12 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered, - sticky, callingPid, callingUid, userId, false /* allowBackgroundActivityStarts */); + sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId, + false /* allowBackgroundActivityStarts */); } @GuardedBy("this") @@ -14380,8 +14387,8 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId, - boolean allowBackgroundActivityStarts) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId, boolean allowBackgroundActivityStarts) { intent = new Intent(intent); final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid); @@ -14430,7 +14437,7 @@ public class ActivityManagerService extends IActivityManager.Stub // PendingIntent), because that who is actually supplied the arguments. if (checkComponentPermission( android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, - Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + realCallingPid, realCallingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: " + intent.getAction() + " broadcast from " + callerPackage + " (pid=" + callingPid @@ -14448,6 +14455,25 @@ public class ActivityManagerService extends IActivityManager.Stub + " has background restrictions"); return ActivityManager.START_CANCELED; } + if (brOptions.allowsBackgroundActivityStarts()) { + // See if the caller is allowed to do this. Note we are checking against + // the actual real caller (not whoever provided the operation as say a + // PendingIntent), because that who is actually supplied the arguments. + if (checkComponentPermission( + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } else { + allowBackgroundActivityStarts = true; + } + } } // Verify that protected broadcasts are only being sent by system code, @@ -15118,15 +15144,15 @@ public class ActivityManagerService extends IActivityManager.Stub callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky, - callingPid, callingUid, userId); + callingPid, callingUid, callingUid, callingPid, userId); Binder.restoreCallingIdentity(origId); return res; } } - int broadcastIntentInPackage(String packageName, int uid, - Intent intent, String resolvedType, IIntentReceiver resultTo, + int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, + int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, int userId, boolean allowBackgroundActivityStarts) { @@ -15139,7 +15165,8 @@ public class ActivityManagerService extends IActivityManager.Stub int res = broadcastIntentLocked(null, packageName, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, OP_NONE, bOptions, serialized, - sticky, -1, uid, userId, allowBackgroundActivityStarts); + sticky, -1, uid, realCallingUid, realCallingPid, userId, + allowBackgroundActivityStarts); Binder.restoreCallingIdentity(origId); return res; } @@ -17721,15 +17748,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int broadcastIntentInPackage(String packageName, int uid, Intent intent, - String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, - boolean sticky, int userId, boolean allowBackgroundActivityStarts) { + public int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, + int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, + int resultCode, String resultData, Bundle resultExtras, String requiredPermission, + Bundle bOptions, boolean serialized, boolean sticky, int userId, + boolean allowBackgroundActivityStarts) { synchronized (ActivityManagerService.this) { return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid, - intent, resolvedType, resultTo, resultCode, resultData, resultExtras, - requiredPermission, bOptions, serialized, sticky, userId, - allowBackgroundActivityStarts); + realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode, + resultData, resultExtras, requiredPermission, bOptions, serialized, sticky, + userId, allowBackgroundActivityStarts); } } @@ -17830,8 +17858,8 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND @@ -17841,8 +17869,8 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } // Send a broadcast to PackageInstallers if the configuration change is interesting @@ -17857,7 +17885,7 @@ public class ActivityManagerService extends IActivityManager.Stub new String[] { android.Manifest.permission.INSTALL_PACKAGES }; broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } } @@ -17881,7 +17909,8 @@ public class ActivityManagerService extends IActivityManager.Stub } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL); + OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -18100,8 +18129,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (!queue.isIdle()) { final String msg = "Waiting for queue " + queue + " to become idle..."; pw.println(msg); + pw.println(queue.describeState()); pw.flush(); Slog.v(TAG, msg); + queue.cancelDeferrals(); idle = false; } } diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index c7e4fc78c013..17ffd9c5e093 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -28,6 +28,7 @@ import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION; import android.app.ActivityManager; import android.app.ActivityThread; +import android.os.Debug; import android.os.Handler; import android.os.Message; import android.os.Process; @@ -410,6 +411,7 @@ public final class AppCompactor { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + ": " + name); + long zramFreeKbBefore = Debug.getZramFreeKb(); long[] rssBefore = Process.getRss(pid); FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); fos.write(action.getBytes()); @@ -417,10 +419,12 @@ public final class AppCompactor { long[] rssAfter = Process.getRss(pid); long end = SystemClock.uptimeMillis(); long time = end - start; + long zramFreeKbAfter = Debug.getZramFreeKb(); EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action, rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, - lastCompactAction, lastCompactTime, msg.arg1, msg.arg2); + lastCompactAction, lastCompactTime, msg.arg1, msg.arg2, + zramFreeKbBefore, zramFreeKbAfter); // Note that as above not taking mPhenoTypeFlagLock here to avoid locking // on every single compaction for a flag that will seldom change and the // impact of reading the wrong value here is low. @@ -429,7 +433,8 @@ public final class AppCompactor { rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, lastCompactAction, lastCompactTime, msg.arg1, - ActivityManager.processStateAmToProto(msg.arg2)); + ActivityManager.processStateAmToProto(msg.arg2), + zramFreeKbBefore, zramFreeKbAfter); } synchronized (mAm) { proc.lastCompactTime = end; 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/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java index 6371cd376d19..0b38ef94de1e 100644 --- a/services/core/java/com/android/server/am/BroadcastDispatcher.java +++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java @@ -65,6 +65,14 @@ public class BroadcastDispatcher { broadcasts.add(br); } + int size() { + return broadcasts.size(); + } + + boolean isEmpty() { + return broadcasts.isEmpty(); + } + void writeToProto(ProtoOutputStream proto, long fieldId) { for (BroadcastRecord br : broadcasts) { br.writeToProto(proto, fieldId); @@ -252,22 +260,48 @@ public class BroadcastDispatcher { synchronized (mLock) { return mCurrentBroadcast == null && mOrderedBroadcasts.isEmpty() - && mDeferredBroadcasts.isEmpty() - && mAlarmBroadcasts.isEmpty(); + && isDeferralsListEmpty(mDeferredBroadcasts) + && isDeferralsListEmpty(mAlarmBroadcasts); + } + } + + private static int pendingInDeferralsList(ArrayList<Deferrals> list) { + int pending = 0; + final int numEntries = list.size(); + for (int i = 0; i < numEntries; i++) { + pending += list.get(i).size(); } + return pending; + } + + private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) { + return pendingInDeferralsList(list) == 0; } /** - * Not quite the traditional size() measurement; includes any in-process but - * not yet retired active outbound broadcast. + * Strictly for logging, describe the currently pending contents in a human- + * readable way */ - public int totalUndelivered() { - synchronized (mLock) { - return mAlarmBroadcasts.size() - + mDeferredBroadcasts.size() - + mOrderedBroadcasts.size() - + (mCurrentBroadcast == null ? 0 : 1); - } + public String describeStateLocked() { + final StringBuilder sb = new StringBuilder(128); + if (mCurrentBroadcast != null) { + sb.append("1 in flight, "); + } + sb.append(mOrderedBroadcasts.size()); + sb.append(" ordered"); + int n = pendingInDeferralsList(mAlarmBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferrals in alarm recipients"); + } + n = pendingInDeferralsList(mDeferredBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferred"); + } + return sb.toString(); } // ---------------------------------- @@ -579,6 +613,26 @@ public class BroadcastDispatcher { } } + /** + * Cancel all current deferrals; that is, make all currently-deferred broadcasts + * immediately deliverable. Used by the wait-for-broadcast-idle mechanism. + */ + public void cancelDeferrals() { + synchronized (mLock) { + zeroDeferralTimes(mAlarmBroadcasts); + zeroDeferralTimes(mDeferredBroadcasts); + } + } + + private static void zeroDeferralTimes(ArrayList<Deferrals> list) { + final int num = list.size(); + for (int i = 0; i < num; i++) { + Deferrals d = list.get(i); + // Safe to do this in-place because it won't break ordering + d.deferUntil = d.deferredBy = 0; + } + } + // ---------------------------------- /** diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index f0b137a6ccb1..d9ea1da79f56 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -898,6 +898,11 @@ public final class BroadcastQueue { for (int i = perms.length-1; i >= 0; i--) { try { PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0); + if (pi == null) { + // a required permission that no package has actually + // defined cannot be signature-required. + return false; + } if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE | PermissionInfo.PROTECTION_FLAG_PRIVILEGED)) != PermissionInfo.PROTECTION_SIGNATURE) { @@ -923,8 +928,8 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " - + mParallelBroadcasts.size() + " parallel broadcasts, " - + mDispatcher.totalUndelivered() + " ordered broadcasts"); + + mParallelBroadcasts.size() + " parallel broadcasts; " + + mDispatcher.describeStateLocked()); mService.updateCpuStats(); @@ -1822,11 +1827,24 @@ public final class BroadcastQueue { record.intent == null ? "" : record.intent.getAction()); } - final boolean isIdle() { + boolean isIdle() { return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty() && (mPendingBroadcast == null); } + // Used by wait-for-broadcast-idle : fast-forward all current deferrals to + // be immediately deliverable. + void cancelDeferrals() { + mDispatcher.cancelDeferrals(); + } + + String describeState() { + synchronized (mService) { + return mParallelBroadcasts.size() + " parallel; " + + mDispatcher.describeStateLocked(); + } + } + void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 8ffb67a1405c..06d015234011 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -79,6 +79,7 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index a71f6af80bec..4b12e43692a6 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -138,4 +138,4 @@ option java_package com.android.server.am 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5) # The task is being compacted -30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2) +30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(AfterZRAMFree|2|2) 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/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index af56352b8cc0..a08c829f0576 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -423,9 +423,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { // If a completion callback has been requested, require // that the broadcast be delivered synchronously int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName, - uid, finalIntent, resolvedType, finishedReceiver, code, null, null, - requiredPermission, options, (finishedReceiver != null), - false, userId, + uid, callingUid, callingPid, finalIntent, resolvedType, + finishedReceiver, code, null, null, requiredPermission, options, + (finishedReceiver != null), false, userId, mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken) || allowTrampoline); if (sent == ActivityManager.BROADCAST_SUCCESS) { diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 3ea114758498..376999dfd80d 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -108,7 +109,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { mIntent.setComponent(componentName); mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID, - Process.SYSTEM_UID, mUserId); + Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); } @Override diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0d49e4cdc9d0..3a61dd987cf5 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1498,6 +1498,13 @@ public final class ProcessList { // Also turn on CheckJNI for debuggable apps. It's quite // awkward to turn on otherwise. runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; + + // Check if the developer does not want ART verification + if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(), + android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) { + runtimeFlags |= Zygote.DISABLE_VERIFIER; + Slog.w(TAG_PROCESSES, app + ": ART verification disabled"); + } } // Run the app in safe mode if its manifest requests so or the // system is booted in safe mode. @@ -1705,9 +1712,15 @@ public final class ProcessList { zygoteProcesses.remove(app); if (zygoteProcesses.size() == 0) { mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG); - Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG); - msg.obj = appZygote; - mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS); + if (app.removed) { + // If we stopped this process because the package hosting it was removed, + // there's no point in delaying the app zygote kill. + killAppZygoteIfNeededLocked(appZygote); + } else { + Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG); + msg.obj = appZygote; + mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS); + } } } } @@ -2162,6 +2175,29 @@ public final class ProcessList { for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } + // See if there are any app zygotes running for this packageName / UID combination, + // and kill it if so. + final ArrayList<AppZygote> zygotesToKill = new ArrayList<>(); + for (SparseArray<AppZygote> appZygotes : mAppZygotes.getMap().values()) { + for (int i = 0; i < appZygotes.size(); ++i) { + final int appZygoteUid = appZygotes.keyAt(i); + if (userId != UserHandle.USER_ALL && UserHandle.getUserId(appZygoteUid) != userId) { + continue; + } + if (appId >= 0 && UserHandle.getAppId(appZygoteUid) != appId) { + continue; + } + final AppZygote appZygote = appZygotes.valueAt(i); + if (packageName != null + && !packageName.equals(appZygote.getAppInfo().packageName)) { + continue; + } + zygotesToKill.add(appZygote); + } + } + for (AppZygote appZygote : zygotesToKill) { + killAppZygoteIfNeededLocked(appZygote); + } mService.updateOomAdjLocked(); return N > 0; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 7f6648a3d174..ac20f6c7eaaf 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -371,7 +371,8 @@ class UserController implements Handler.Callback { | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), userId); } // We need to delay unlocking managed profiles until the parent user @@ -471,7 +472,7 @@ class UserController implements Handler.Callback { Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); mInjector.broadcastIntent(unlockedIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - userId); + Binder.getCallingUid(), Binder.getCallingPid(), userId); if (getUserInfo(userId).isManagedProfile()) { UserInfo parent = mInjector.getUserManager().getProfileParent(userId); @@ -484,8 +485,8 @@ class UserController implements Handler.Callback { | Intent.FLAG_RECEIVER_FOREGROUND); mInjector.broadcastIntent(profileUnlockedIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, - parent.id); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), parent.id); } } @@ -543,7 +544,8 @@ class UserController implements Handler.Callback { mInjector.getUserManager().makeInitialized(userInfo.id); } }, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), userId); } } @@ -573,7 +575,8 @@ class UserController implements Handler.Callback { } }, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), userId); } int restartUser(final int userId, final boolean foreground) { @@ -696,7 +699,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(stoppingIntent, null, stoppingReceiver, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); }); } } @@ -735,7 +739,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(shutdownIntent, null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), userId); } void finishUserStopped(UserState uss) { @@ -834,7 +839,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } /** @@ -950,6 +956,8 @@ class UserController implements Handler.Callback { Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { final int oldUserId = getCurrentUserId(); @@ -1088,7 +1096,7 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, userId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId); } if (foreground) { @@ -1111,7 +1119,8 @@ class UserController implements Handler.Callback { } }, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -1427,6 +1436,8 @@ class UserController implements Handler.Callback { } void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); long ident = Binder.clearCallingIdentity(); try { Intent intent; @@ -1442,7 +1453,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, profileUserId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + profileUserId); } } if (newUserId >= 0) { @@ -1457,7 +1469,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, profileUserId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + profileUserId); } intent = new Intent(Intent.ACTION_USER_SWITCHED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY @@ -1466,8 +1479,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(intent, null, null, 0, null, null, new String[] {android.Manifest.permission.MANAGE_USERS}, - AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, + callingPid, UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -2107,12 +2120,14 @@ class UserController implements Handler.Callback { protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { // TODO b/64165549 Verify that mLock is not held before calling AMS methods synchronized (mService) { return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, - ordered, sticky, callingPid, callingUid, userId); + ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, + userId); } } 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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 0e3309088f21..70c28a854512 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -38,6 +38,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; +import android.app.AppOpsManager.HistoricalOpsRequest; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.content.BroadcastReceiver; @@ -1024,25 +1025,29 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void getHistoricalOps(int uid, @NonNull String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, @NonNull RemoteCallback callback) { - Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0, - "uid must be " + Process.INVALID_UID + " or non negative"); - Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis, - "beginTimeMillis must be non negative and lesser than endTimeMillis"); + // Use the builder to validate arguments. + final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + beginTimeMillis, endTimeMillis) + .setUid(uid) + .setPackageName(packageName) + .setOpNames(opNames) + .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); - checkValidOpsOrNull(opNames); mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + final String[] opNamesArray = (opNames != null) + ? opNames.toArray(new String[opNames.size()]) : null; if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) { // TODO (bug:122218838): Remove once the feature fully enabled. - getHistoricalPackagesOpsCompat(uid, packageName, opNames, beginTimeMillis, + getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } else { // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOps(uid, packageName, opNames, + mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } } @@ -1101,20 +1106,25 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, @NonNull RemoteCallback callback) { - Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0, - "uid must be " + Process.INVALID_UID + " or non negative"); - Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis, - "beginTimeMillis must be non negative and lesser than endTimeMillis"); + // Use the builder to validate arguments. + final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + beginTimeMillis, endTimeMillis) + .setUid(uid) + .setPackageName(packageName) + .setOpNames(opNames) + .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); - checkValidOpsOrNull(opNames); mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + final String[] opNamesArray = (opNames != null) + ? opNames.toArray(new String[opNames.size()]) : null; + // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, + mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } @@ -4266,16 +4276,6 @@ public class AppOpsService extends IAppOpsService.Stub { return packageNames; } - private static void checkValidOpsOrNull(String[] opNames) { - if (opNames != null) { - for (String opName : opNames) { - if (AppOpsManager.strOpToOp(opName) == AppOpsManager.OP_NONE) { - throw new IllegalArgumentException("Unknown op: " + opName); - } - } - } - } - private final class ClientRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; 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 8995068ef504..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,75 +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 { + 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 onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) - throws RemoteException { - // TODO + @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 @@ -647,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); + } }; @@ -670,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 @@ -693,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; @@ -761,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 @@ -780,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 @@ -790,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. } @@ -799,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) { @@ -828,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); @@ -865,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..59fffb40fba7 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; @@ -895,6 +918,7 @@ public class Vpn { TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator); TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator); for (final RouteInfo route : routes) { + if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue; IpPrefix destination = route.getDestination(); if (destination.isIPv4()) { ipv4Prefixes.add(destination); @@ -1007,9 +1031,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 +1124,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 +1279,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 +1294,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 +1306,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 +1322,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 +1537,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 +1559,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/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 6ee5665b9e42..e9ae516cc8d0 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; +import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; @@ -225,8 +226,12 @@ abstract class DisplayDevice { viewport.deviceHeight = isRotated ? info.width : info.height; viewport.uniqueId = info.uniqueId; - // TODO(b/112898898) Use an actual port here. - viewport.physicalPort = null; + + if (info.address instanceof DisplayAddress.Physical) { + viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort(); + } else { + viewport.physicalPort = null; + } } /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index ab64f61a3b22..729ea1772066 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.hardware.display.DisplayViewport; import android.util.DisplayMetrics; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.Surface; @@ -274,7 +275,7 @@ final class DisplayDeviceInfo { * Display address, or null if none. * Interpretation varies by display type. */ - public String address; + public DisplayAddress address; /** * Display state. 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/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 28f21f633ac4..489194726c5a 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -31,6 +31,7 @@ import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.Surface; @@ -382,6 +383,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos; mInfo.state = mState; mInfo.uniqueId = getUniqueId(); + mInfo.address = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); // Assume that all built-in displays that have secure output (eg. HDCP) also // support compositing from gralloc protected buffers. diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index e65637f04975..2f507d17730e 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -308,7 +308,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; } mInfo.type = Display.TYPE_OVERLAY; - mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; + mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL; mInfo.state = mState; } return mInfo; diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index e8d6ad455fbf..9e4c1cb57dca 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -16,9 +16,6 @@ package com.android.server.display; -import com.android.internal.util.DumpUtils; -import com.android.internal.util.IndentingPrintWriter; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -35,9 +32,13 @@ import android.os.Message; import android.os.UserHandle; import android.util.Slog; import android.view.Display; +import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -581,7 +582,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { private final int mHeight; private final float mRefreshRate; private final int mFlags; - private final String mAddress; + private final DisplayAddress mAddress; private final Display.Mode mMode; private Surface mSurface; @@ -596,7 +597,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { mHeight = height; mRefreshRate = refreshRate; mFlags = flags; - mAddress = address; + mAddress = DisplayAddress.fromMacAddress(address); mSurface = surface; mMode = createMode(width, height, refreshRate); } 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/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index a68ceed750ad..6899c3ffcbb1 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -22,24 +22,33 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import android.annotation.NonNull; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.gamedriver.GameDriverProto.Blacklist; +import android.gamedriver.GameDriverProto.Blacklists; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.util.Base64; import android.util.Slog; +import com.android.framework.protobuf.InvalidProtocolBufferException; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; /** * Service to manage GPU related features. @@ -52,17 +61,25 @@ public class GpuService extends SystemService { public static final boolean DEBUG = false; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; + private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; private final Context mContext; private final String mDriverPackageName; private final PackageManager mPackageManager; + private final Object mLock = new Object(); + private ContentResolver mContentResolver; + private long mGameDriverVersionCode; + private SettingsObserver mSettingsObserver; + @GuardedBy("mLock") + private Blacklists mBlacklists; public GpuService(Context context) { super(context); mContext = context; mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); + mGameDriverVersionCode = -1; mPackageManager = context.getPackageManager(); if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) { final IntentFilter packageFilter = new IntentFilter(); @@ -82,10 +99,37 @@ public class GpuService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { + mContentResolver = mContext.getContentResolver(); + mSettingsObserver = new SettingsObserver(); if (mDriverPackageName == null || mDriverPackageName.isEmpty()) { return; } fetchGameDriverPackageProperties(); + processBlacklists(); + setBlacklist(); + } + } + + private final class SettingsObserver extends ContentObserver { + private final Uri mGameDriverBlackUri = + Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS); + + SettingsObserver() { + super(new Handler()); + mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mGameDriverBlackUri.equals(uri)) { + processBlacklists(); + setBlacklist(); + } } } @@ -109,6 +153,7 @@ public class GpuService extends SystemService { case ACTION_PACKAGE_CHANGED: case ACTION_PACKAGE_REMOVED: fetchGameDriverPackageProperties(); + setBlacklist(); break; default: // do nothing @@ -138,16 +183,22 @@ public class GpuService extends SystemService { return; } + // Reset the whitelist. + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_WHITELIST, ""); + mGameDriverVersionCode = driverInfo.longVersionCode; + try { final Context driverContext = mContext.createPackageContext(mDriverPackageName, Context.CONTEXT_RESTRICTED); final BufferedReader reader = new BufferedReader( - new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME))); + new InputStreamReader(driverContext.getAssets() + .open(GAME_DRIVER_WHITELIST_FILENAME))); final ArrayList<String> whitelistedPackageNames = new ArrayList<>(); for (String packageName; (packageName = reader.readLine()) != null; ) { whitelistedPackageNames.add(packageName); } - Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_WHITELIST, String.join(",", whitelistedPackageNames)); } catch (PackageManager.NameNotFoundException e) { @@ -160,4 +211,48 @@ public class GpuService extends SystemService { } } } + + private void processBlacklists() { + // TODO(b/121350991) Switch to DeviceConfig with property listener. + String base64String = + Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS); + if (base64String == null || base64String.isEmpty()) { + return; + } + + synchronized (mLock) { + // Reset all blacklists + mBlacklists = null; + try { + mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); + } catch (IllegalArgumentException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } catch (InvalidProtocolBufferException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } + } + } + + private void setBlacklist() { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, ""); + synchronized (mLock) { + if (mBlacklists == null) { + return; + } + List<Blacklist> blacklists = mBlacklists.getBlacklistsList(); + for (Blacklist blacklist : blacklists) { + if (blacklist.getVersionCode() == mGameDriverVersionCode) { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, + String.join(",", blacklist.getPackageNamesList())); + return; + } + } + } + } } diff --git a/services/core/java/com/android/server/input/InputForwarder.java b/services/core/java/com/android/server/input/InputForwarder.java deleted file mode 100644 index 00af8398d0ff..000000000000 --- a/services/core/java/com/android/server/input/InputForwarder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.input; - -import android.app.IInputForwarder; -import android.hardware.input.InputManagerInternal; -import android.view.InputEvent; - -import com.android.server.LocalServices; - -import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; - -/** - * Basic implementation of {@link IInputForwarder}. - */ -class InputForwarder extends IInputForwarder.Stub { - - private final InputManagerInternal mInputManagerInternal; - private final int mDisplayId; - - InputForwarder(int displayId) { - mDisplayId = displayId; - mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); - } - - @Override - public boolean forwardEvent(InputEvent event) { - event.setDisplayId(mDisplayId); - return mInputManagerInternal.injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC); - } -}
\ No newline at end of file diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 28393a209111..87c744138797 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,7 +17,6 @@ package com.android.server.input; import android.annotation.NonNull; -import android.app.IInputForwarder; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -1685,29 +1684,6 @@ public class InputManagerService extends IInputManager.Stub nativeMonitor(mPtr); } - // Binder call - @Override - public IInputForwarder createInputForwarder(int displayId) throws RemoteException { - if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS, - "createInputForwarder()")) { - throw new SecurityException("Requires INJECT_EVENTS permission"); - } - final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); - final Display display = displayManager.getDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException( - "Can't create input forwarder for non-existent displayId: " + displayId); - } - final int callingUid = Binder.getCallingUid(); - final int displayOwnerUid = display.getOwnerUid(); - if (callingUid != displayOwnerUid) { - throw new SecurityException( - "Only owner of the display can forward input events to it."); - } - - return new InputForwarder(displayId); - } - // Native callback. private void notifyConfigurationChanged(long whenNanos) { mWindowManagerCallbacks.notifyConfigurationChanged(); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 3abacc2c9c10..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; } @@ -917,7 +917,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements synchronized (mLock) { boolean enabled = ((mProviderRequest != null && mProviderRequest.reportLocation - && mProviderRequest.forceLocation) || ( + && mProviderRequest.locationSettingsIgnored) || ( mContext.getSystemService(LocationManager.class).isLocationEnabled() && !mDisableGps)) && !mShutdown; if (enabled == mEnabled) { 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/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java index c62a31e24fb2..ff22a8dc934f 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java @@ -20,6 +20,8 @@ import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import org.w3c.dom.Element; + import java.security.SecureRandom; import java.security.cert.CertPath; import java.security.cert.X509Certificate; @@ -28,8 +30,6 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import org.w3c.dom.Element; - /** * Parses and holds the XML file containing the list of THM public-key certificates and related * metadata. @@ -38,24 +38,20 @@ public final class CertXml { private static final String METADATA_NODE_TAG = "metadata"; private static final String METADATA_SERIAL_NODE_TAG = "serial"; - private static final String METADATA_REFRESH_INTERVAL_NODE_TAG = "refresh-interval"; private static final String ENDPOINT_CERT_LIST_TAG = "endpoints"; private static final String ENDPOINT_CERT_ITEM_TAG = "cert"; private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates"; private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert"; private final long serial; - private final long refreshInterval; private final List<X509Certificate> intermediateCerts; private final List<X509Certificate> endpointCerts; private CertXml( long serial, - long refreshInterval, List<X509Certificate> intermediateCerts, List<X509Certificate> endpointCerts) { this.serial = serial; - this.refreshInterval = refreshInterval; this.intermediateCerts = intermediateCerts; this.endpointCerts = endpointCerts; } @@ -65,15 +61,6 @@ public final class CertXml { return serial; } - /** - * Gets the refresh interval in the XML file containing public-key certificates. The refresh - * interval denotes the number of seconds that the client should follow to contact the server to - * refresh the XML file. - */ - public long getRefreshInterval() { - return refreshInterval; - } - @VisibleForTesting List<X509Certificate> getAllIntermediateCerts() { return intermediateCerts; @@ -121,7 +108,6 @@ public final class CertXml { Element rootNode = CertUtils.getXmlRootNode(bytes); return new CertXml( parseSerial(rootNode), - parseRefreshInterval(rootNode), parseIntermediateCerts(rootNode), parseEndpointCerts(rootNode)); } @@ -136,16 +122,6 @@ public final class CertXml { return Long.parseLong(contents.get(0)); } - private static long parseRefreshInterval(Element rootNode) throws CertParsingException { - List<String> contents = - CertUtils.getXmlNodeContents( - CertUtils.MUST_EXIST_EXACTLY_ONE, - rootNode, - METADATA_NODE_TAG, - METADATA_REFRESH_INTERVAL_NODE_TAG); - return Long.parseLong(contents.get(0)); - } - private static List<X509Certificate> parseIntermediateCerts(Element rootNode) throws CertParsingException { List<String> contents = 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/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index dac4b6ff39c3..8051abbdd417 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -27,6 +27,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; @@ -50,8 +51,12 @@ class ApexManager { private final Map<String, PackageInfo> mActivePackagesCache; ApexManager() { - mApexService = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); + try { + mApexService = IApexService.Stub.asInterface( + ServiceManager.getServiceOrThrow("apexservice")); + } catch (ServiceNotFoundException e) { + throw new IllegalStateException("Required service apexservice not available"); + } mActivePackagesCache = populateActivePackagesCache(); } @@ -151,7 +156,7 @@ class ApexManager { } /** - * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be + * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be * applied at next reboot. * * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. @@ -167,6 +172,27 @@ class ApexManager { } /** + * Marks a staged session as successful. + * + * <p>Only activated session can be marked as successful. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as + * successful. + */ + void markStagedSessionSuccessful(int sessionId) { + try { + mApexService.markStagedSessionSuccessful(sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } catch (Exception e) { + // It is fine to just log an exception in this case. APEXd will be able to recover in + // case markStagedSessionSuccessful fails. + Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e); + } + } + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -196,7 +222,7 @@ class ApexManager { ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); for (ApexSessionInfo si : sessions) { - ipw.println("Session ID: " + Integer.toString(si.sessionId)); + ipw.println("Session ID: " + si.sessionId); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); @@ -206,8 +232,6 @@ class ApexManager { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); - } else if (si.isActivationPendingRetry) { - ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java index 5b765dfee3a4..d53d81cf0860 100644 --- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java +++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java @@ -28,7 +28,7 @@ import android.util.ByteStringUtils; import android.util.EventLog; import android.util.Log; -import com.android.server.pm.dex.DexLogger; +import com.android.server.pm.dex.DynamicCodeLogger; import java.util.ArrayList; import java.util.List; @@ -38,9 +38,10 @@ import java.util.regex.Pattern; /** * Scheduled jobs related to logging of app dynamic code loading. The idle logging job runs daily - * while idle and charging and calls {@link DexLogger} to write dynamic code information to the - * event log. The audit watching job scans the event log periodically while idle to find AVC audit - * messages indicating use of dynamic native code and adds the information to {@link DexLogger}. + * while idle and charging and calls {@link DynamicCodeLogger} to write dynamic code information + * to the event log. The audit watching job scans the event log periodically while idle to find AVC + * audit messages indicating use of dynamic native code and adds the information to + * {@link DynamicCodeLogger}. * {@hide} */ public class DynamicCodeLoggingService extends JobService { @@ -130,9 +131,9 @@ public class DynamicCodeLoggingService extends JobService { } } - private static DexLogger getDexLogger() { + private static DynamicCodeLogger getDynamicCodeLogger() { PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package"); - return pm.getDexManager().getDexLogger(); + return pm.getDexManager().getDynamicCodeLogger(); } private class IdleLoggingThread extends Thread { @@ -149,14 +150,14 @@ public class DynamicCodeLoggingService extends JobService { Log.d(TAG, "Starting IdleLoggingJob run"); } - DexLogger dexLogger = getDexLogger(); - for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) { + DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger(); + for (String packageName : dynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) { if (mIdleLoggingStopRequested) { Log.w(TAG, "Stopping IdleLoggingJob run at scheduler request"); return; } - dexLogger.logDynamicCodeLoading(packageName); + dynamicCodeLogger.logDynamicCodeLoading(packageName); } jobFinished(mParams, /* reschedule */ false); @@ -191,7 +192,7 @@ public class DynamicCodeLoggingService extends JobService { private boolean processAuditEvents() { // Scan the event log for SELinux (avc) audit messages indicating when an // (untrusted) app has executed native code from an app data - // file. Matches are recorded in DexLogger. + // file. Matches are recorded in DynamicCodeLogger. // // These messages come from the kernel audit system via logd. (Note that // some devices may not generate these messages at all, or the format may @@ -213,7 +214,7 @@ public class DynamicCodeLoggingService extends JobService { // On each run we process all the matching events in the log. This may // mean re-processing events we have already seen, and in any case there // may be duplicate events for the same app+file. These are de-duplicated - // by DexLogger. + // by DynamicCodeLogger. // // Note that any app can write a message to the event log, including one // that looks exactly like an AVC audit message, so the information may @@ -228,7 +229,7 @@ public class DynamicCodeLoggingService extends JobService { return true; } - DexLogger dexLogger = getDexLogger(); + DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger(); List<EventLog.Event> events = new ArrayList<>(); EventLog.readEvents(tags, events); @@ -267,7 +268,7 @@ public class DynamicCodeLoggingService extends JobService { // hex-encodes the bytes; we need to undo that. path = unhex(matcher.group(2)); } - dexLogger.recordNative(uid, path); + dynamicCodeLogger.recordNative(uid, path); } return true; diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 33b8641c145e..8ce2568ac3e2 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -9,6 +9,10 @@ svetoslavganov@google.com toddke@android.com toddke@google.com +# apex support +per-file ApexManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com +per-file StagingManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com + # dex per-file AbstractStatsBase.java = agampe@google.com per-file AbstractStatsBase.java = calin@google.com @@ -19,6 +23,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 400443a16fab..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); } } } @@ -3306,7 +3309,8 @@ public class PackageManagerService extends IPackageManager.Stub // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", - "persist.sys.isolated_storage"); + StorageManager.PROP_ISOLATED_STORAGE, + StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -12803,7 +12807,7 @@ public class PackageManagerService extends IPackageManager.Stub public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, int restrictionFlags, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, - "setPackagesSuspendedAsUser"); + "setDistractingPackageRestrictionsAsUser"); final int callingUid = Binder.getCallingUid(); if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID @@ -18817,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) { @@ -19076,6 +19080,7 @@ public class PackageManagerService extends IPackageManager.Stub pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19226,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++) { @@ -19254,6 +19266,9 @@ public class PackageManagerService extends IPackageManager.Stub pir.removeFilter(pa); } changed = true; + if (!skipUpdateDefaultHome) { + updateDefaultHomeLPw(thisUserId); + } } } if (changed) { @@ -19313,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 @@ -19383,6 +19399,7 @@ public class PackageManagerService extends IPackageManager.Stub new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19426,6 +19443,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } } @@ -19513,6 +19531,7 @@ public class PackageManagerService extends IPackageManager.Stub (readParser, readUserId) -> { synchronized (mPackages) { mSettings.readPreferredActivitiesLPw(readParser, readUserId); + updateDefaultHomeLPw(readUserId); } }); } catch (Exception e) { @@ -19568,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) { @@ -19633,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( @@ -19927,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 @@ -21213,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. @@ -23829,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..d1ebc9400e4d 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."); @@ -250,7 +247,8 @@ public class StagingManager { } private void resumeSession(@NonNull PackageInstallerSession session) { - if (sessionContainsApex(session)) { + boolean hasApex = sessionContainsApex(session); + if (hasApex) { // Check with apexservice whether the apex packages have been activated. ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); if (apexSessionInfo == null) { @@ -274,7 +272,7 @@ public class StagingManager { mBgHandler.post(() -> preRebootVerification(session)); return; } - if (!apexSessionInfo.isActivated) { + if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { // In all the remaining cases apexd will try to apply the session again at next // boot. Nothing to do here for now. Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied " @@ -290,7 +288,11 @@ public class StagingManager { + "more information."); return; } + session.setStagedSessionApplied(); + if (hasApex) { + mApexManager.markStagedSessionSuccessful(session.sessionId); + } } private String findFirstAPKInDir(File stageDir) { diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index ee6995b11430..3b805d515178 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -86,11 +86,11 @@ public class DexManager { // encode and save the dex usage data. private final PackageDexUsage mPackageDexUsage; - // DexLogger handles recording of dynamic code loading - which is similar to PackageDexUsage - // but records a different aspect of the data. + // DynamicCodeLogger handles recording of dynamic code loading - which is similar to + // PackageDexUsage but records a different aspect of the data. // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't // record class loaders or ISAs.) - private final DexLogger mDexLogger; + private final DynamicCodeLogger mDynamicCodeLogger; private final IPackageManager mPackageManager; private final PackageDexOptimizer mPackageDexOptimizer; @@ -126,11 +126,11 @@ public class DexManager { mPackageDexOptimizer = pdo; mInstaller = installer; mInstallLock = installLock; - mDexLogger = new DexLogger(pms, installer); + mDynamicCodeLogger = new DynamicCodeLogger(pms, installer); } - public DexLogger getDexLogger() { - return mDexLogger; + public DynamicCodeLogger getDynamicCodeLogger() { + return mDynamicCodeLogger; } /** @@ -230,8 +230,8 @@ public class DexManager { if (!primaryOrSplit) { // Record loading of a DEX file from an app data directory. - mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName, - loadingAppInfo.packageName); + mDynamicCodeLogger.recordDex(loaderUserId, dexPath, + searchResult.mOwningPackageName, loadingAppInfo.packageName); } if (classLoaderContexts != null) { @@ -269,7 +269,7 @@ public class DexManager { loadInternal(existingPackages); } catch (Exception e) { mPackageDexUsage.clear(); - mDexLogger.clear(); + mDynamicCodeLogger.clear(); Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e); } } @@ -320,12 +320,12 @@ public class DexManager { if (mPackageDexUsage.removePackage(packageName)) { mPackageDexUsage.maybeWriteAsync(); } - mDexLogger.removePackage(packageName); + mDynamicCodeLogger.removePackage(packageName); } else { if (mPackageDexUsage.removeUserPackage(packageName, userId)) { mPackageDexUsage.maybeWriteAsync(); } - mDexLogger.removeUserPackage(packageName, userId); + mDynamicCodeLogger.removeUserPackage(packageName, userId); } } @@ -404,9 +404,9 @@ public class DexManager { } try { - mDexLogger.readAndSync(packageToUsersMap); + mDynamicCodeLogger.readAndSync(packageToUsersMap); } catch (Exception e) { - mDexLogger.clear(); + mDynamicCodeLogger.clear(); Slog.w(TAG, "Exception while loading package dynamic code usage. " + "Starting with a fresh state.", e); } @@ -692,7 +692,7 @@ public class DexManager { */ public void writePackageDexUsageNow() { mPackageDexUsage.writeNow(); - mDexLogger.writeNow(); + mDynamicCodeLogger.writeNow(); } /** diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java index 59cc0cfeef45..2c75bcdaa1ef 100644 --- a/services/core/java/com/android/server/pm/dex/DexLogger.java +++ b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.server.pm.dex; @@ -44,12 +44,12 @@ import java.util.Map; import java.util.Set; /** - * This class is responsible for logging data about secondary dex files and, despite the name, - * native code executed from an app's private directory. The data logged includes hashes of the - * name and content of each file. + * This class is responsible for logging data about secondary dex files and native code executed + * from an app's private directory. The data logged includes hashes of the name and content of each + * file. */ -public class DexLogger { - private static final String TAG = "DexLogger"; +public class DynamicCodeLogger { + private static final String TAG = "DynamicCodeLogger"; // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) - // see b/63927552. @@ -61,12 +61,12 @@ public class DexLogger { private final PackageDynamicCodeLoading mPackageDynamicCodeLoading; private final Installer mInstaller; - public DexLogger(IPackageManager pms, Installer installer) { + DynamicCodeLogger(IPackageManager pms, Installer installer) { this(pms, installer, new PackageDynamicCodeLoading()); } @VisibleForTesting - DexLogger(IPackageManager pms, Installer installer, + DynamicCodeLogger(IPackageManager pms, Installer installer, PackageDynamicCodeLoading packageDynamicCodeLoading) { mPackageManager = pms; mPackageDynamicCodeLoading = packageDynamicCodeLoading; @@ -217,7 +217,7 @@ public class DexLogger { /** * Record that an app running in the specified uid has executed native code from the file at - * {@link path}. + * {@param path}. */ public void recordNative(int loadingUid, String path) { String[] packages; diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 2036ed73fdf2..dacc6cd0fe76 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -721,7 +721,7 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage( getDefaultSystemHandlerActivityPackage( RingtoneManager.ACTION_RINGTONE_PICKER, userId), - userId, STORAGE_PERMISSIONS); + userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS); // TextClassifier Service String textClassifierPackageName = @@ -1462,14 +1462,15 @@ public final class DefaultPermissionGrantPolicy { outGrantExceptions.get(packageName); if (packageExceptions == null) { // The package must be on the system image - if (!isSystemPackage(packageName)) { - Log.w(TAG, "Unknown package:" + packageName); + PackageInfo packageInfo = getSystemPackageInfo(packageName); + if (!isSystemPackage(packageInfo)) { + Log.w(TAG, "Unknown system package:" + packageName); XmlUtils.skipCurrentTag(parser); continue; } // The package must support runtime permissions - if (!doesPackageSupportRuntimePermissions(getSystemPackageInfo(packageName))) { + if (!doesPackageSupportRuntimePermissions(packageInfo)) { Log.w(TAG, "Skipping non supporting runtime permissions package:" + packageName); XmlUtils.skipCurrentTag(parser); 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..8df5a71de43e 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); @@ -1095,6 +1182,8 @@ public class PermissionManagerService { AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); String pkgName = pkg.packageName; + boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion + >= Build.VERSION_CODES.M; int[] users = UserManagerService.getInstance().getUserIds(); int numUsers = users.length; @@ -1119,27 +1208,17 @@ 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 { + if (supportsRuntimePermissions) { int revokeResult = ps.revokeRuntimePermission(bp, userId); - if (revokeResult - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - + if (revokeResult != PERMISSION_OPERATION_FAILURE) { if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " + permission - + " for " + pkgName + Slog.i(TAG, "Revoking runtime permission " + + permission + " for " + pkgName + " as it is now requested"); } } + } else { + setAppOpMode(permission, pkg, userId, MODE_IGNORED); } List<String> fgPerms = mBackgroundPermissions.get(permission); @@ -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/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 1782b6a8aa78..176dbbf6a965 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -538,6 +538,9 @@ public final class PowerManagerService extends SystemService // True if we are currently in VR Mode. private boolean mIsVrModeEnabled; + // True if we in the process of performing a forceSuspend + private boolean mForceSuspendActive; + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(int newUserId) throws RemoteException {} @@ -684,6 +687,11 @@ public final class PowerManagerService extends SystemService public void nativeSetFeature(int featureId, int data) { PowerManagerService.nativeSetFeature(featureId, data); } + + /** Wrapper for PowerManager.nativeForceSuspend */ + public boolean nativeForceSuspend() { + return PowerManagerService.nativeForceSuspend(); + } } @VisibleForTesting @@ -718,6 +726,7 @@ public final class PowerManagerService extends SystemService private static native void nativeSetAutoSuspend(boolean enable); private static native void nativeSendPowerHint(int hintId, int data); private static native void nativeSetFeature(int featureId, int data); + private static native boolean nativeForceSuspend(); public PowerManagerService(Context context) { this(context, new Injector()); @@ -1427,7 +1436,7 @@ public final class PowerManagerService extends SystemService } if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE - || !mBootCompleted || !mSystemReady) { + || !mBootCompleted || !mSystemReady || mForceSuspendActive) { return false; } @@ -1463,8 +1472,13 @@ public final class PowerManagerService extends SystemService } } - // This method is called goToSleep for historical reasons but we actually start - // dozing before really going to sleep. + /** + * Puts the system in doze. + * + * This method is called goToSleep for historical reasons but actually attempts to DOZE, + * and only tucks itself in to SLEEP if requested with the flag + * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}. + */ @SuppressWarnings("deprecation") private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) { if (DEBUG_SPEW) { @@ -1481,35 +1495,10 @@ public final class PowerManagerService extends SystemService Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); try { - switch (reason) { - case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: - Slog.i(TAG, "Going to sleep due to device administration policy " - + "(uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: - Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH: - Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON: - Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON: - Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_HDMI: - Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY: - Slog.i(TAG, "Going to sleep by an accessibility service request (uid " - + uid +")..."); - break; - default: - Slog.i(TAG, "Going to sleep by application request (uid " + uid +")..."); - reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION; - break; - } + reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX, + Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN)); + Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) + + " (uid " + uid + ")..."); mLastSleepTime = eventTime; mLastSleepReason = reason; @@ -3063,10 +3052,10 @@ public final class PowerManagerService extends SystemService if (appid >= Process.FIRST_APPLICATION_UID) { // Cached inactive processes are never allowed to hold wake locks. if (mConstants.NO_CACHED_WAKE_LOCKS) { - disabled = !wakeLock.mUidState.mActive && - wakeLock.mUidState.mProcState + disabled = mForceSuspendActive + || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT && - wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER; + wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER); } if (mDeviceIdleMode) { // If we are in idle mode, we will also ignore all partial wake locks that are @@ -3241,6 +3230,34 @@ public final class PowerManagerService extends SystemService } } + private boolean forceSuspendInternal(int uid) { + try { + synchronized (mLock) { + mForceSuspendActive = true; + // Place the system in an non-interactive state + goToSleepInternal(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + + // Disable all the partial wake locks as well + updateWakeLockDisabledStatesLocked(); + } + + Slog.i(TAG, "Force-Suspending (uid " + uid + ")..."); + boolean success = mNativeWrapper.nativeForceSuspend(); + if (!success) { + Slog.i(TAG, "Force-Suspending failed in native."); + } + return success; + } finally { + synchronized (mLock) { + mForceSuspendActive = false; + // Re-enable wake locks once again. + updateWakeLockDisabledStatesLocked(); + } + } + } + /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. @@ -4743,6 +4760,20 @@ public final class PowerManagerService extends SystemService } } + @Override // binder call + public boolean forceSuspend() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + return forceSuspendInternal(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override // Binder call protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 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..05d3c17e5c25 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.rollback; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -40,6 +41,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 +63,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 +74,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 +493,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 +592,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (RollbackInfo info : mRecentlyExecutedRollbacks) { mAllocatedRollbackIds.put(info.getRollbackId(), true); } - - scheduleExpiration(0); } /** @@ -700,8 +725,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 +735,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)); } } @@ -819,6 +843,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { String packageName = newPackage.packageName; for (PackageRollbackInfo info : rd.packages) { if (info.getPackageName().equals(packageName)) { + info.getInstalledUsers().addAll(IntArray.wrap(installedUsers)); AppDataRollbackHelper.SnapshotAppDataResult rs = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); info.getPendingBackups().addAll(rs.pendingBackups); @@ -851,7 +876,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * the child sessions, not the parent session. */ private boolean enableRollbackForSession(PackageInstaller.SessionInfo session, - int[] installedUsers, boolean snapshotUserData) { + @NonNull int[] installedUsers, boolean snapshotUserData) { // TODO: Don't attempt to enable rollback for split installs. final int installFlags = session.installFlags; if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) { @@ -993,7 +1018,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (!session.isMultiPackage()) { - if (!enableRollbackForSession(session, null, false)) { + if (!enableRollbackForSession(session, new int[0], false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1007,7 +1032,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { result.offer(false); return; } - if (!enableRollbackForSession(childSession, null, false)) { + if (!enableRollbackForSession(childSession, new int[0], false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1144,8 +1169,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/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING index c1d95ac85c23..6be93a0a199b 100644 --- a/services/core/java/com/android/server/rollback/TEST_MAPPING +++ b/services/core/java/com/android/server/rollback/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "RollbackTest" + }, + { + "name": "StagedRollbackTest" } ] } 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/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 423ec4c869c1..c7044a15dd5d 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -366,17 +366,22 @@ public class TrustManagerService extends SystemService { } catch (RemoteException e) { } - if (mSettingsObserver.getTrustAgentsExtendUnlock()) { - trusted = trusted && (!showingKeyguard || isFromUnlock) && userId == mCurrentUser; - if (DEBUG) { - Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted) - + " && " + Boolean.toString(!showingKeyguard) - + " && " + Boolean.toString(userId == mCurrentUser)); - } - } - boolean changed; synchronized (mUserIsTrusted) { + if (mSettingsObserver.getTrustAgentsExtendUnlock()) { + // In extend unlock trust agents can only set the device to trusted if it already + // trusted or the device is unlocked. Attempting to set the device as trusted + // when the device is locked will be ignored. + changed = mUserIsTrusted.get(userId) != trusted; + trusted = trusted + && (!showingKeyguard || isFromUnlock || !changed) + && userId == mCurrentUser; + if (DEBUG) { + Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted) + + " && " + Boolean.toString(!showingKeyguard) + + " && " + Boolean.toString(userId == mCurrentUser)); + } + } changed = mUserIsTrusted.get(userId) != trusted; mUserIsTrusted.put(userId, trusted); } diff --git a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java new file mode 100644 index 000000000000..f26121eac939 --- /dev/null +++ b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java @@ -0,0 +1,133 @@ +/* + * 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.utils; + +import android.annotation.Nullable; +import android.provider.DeviceConfig; + +import com.android.server.RescueParty; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utilities for interacting with the {@link android.provider.DeviceConfig}. + * + * @hide + */ +public final class FlagNamespaceUtils { + /** + * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that + * Settings were reset by the RescueParty, no actual namespace with this name exists in + * {@link DeviceConfig}. + */ + public static final String NAMESPACE_NO_PACKAGE = "no_package"; + + /** + * Name of the special namespace in DeviceConfig table used for communicating resets. + */ + private static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace"; + /** + * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link + * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the + * first time flags are written to the new namespace in the {@link DeviceConfig}. + */ + private static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces"; + /** + * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter + * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently + * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given + * namespace flags are reset. + */ + private static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package"; + private static final String DELIMITER = ":"; + /** + * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG} + * when communicating recently reset by the RescueParty namespace values. + */ + private static final int MAX_COUNTER_VALUE = 50; + + private static int sKnownResetNamespacesFlagCounter = -1; + + /** + * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with + * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace + * in the consumed namespacesList. These flags are used for communicating the namespaces + * (aka platform packages) whose flags in {@link DeviceConfig} were just reset + * by the RescueParty. + */ + public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) { + if (namespacesList == null) { + return; + } + for (String namespace : namespacesList) { + addToKnownResetNamespaces(namespace); + } + } + + /** + * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with + * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace. + * This flag is used for communicating the namespace (aka platform package) whose flags + * in {@link DeviceConfig} were just reset by the RescueParty. + */ + public static void addToKnownResetNamespaces(String namespace) { + int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter(); + DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY, + RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter, + namespace, /*makeDefault=*/ true); + } + + /** + * Reset all namespaces in DeviceConfig with consumed resetMode. + */ + public static void resetDeviceConfig(int resetMode) { + List<String> allKnownNamespaces = getAllKnownDeviceConfigNamespacesList(); + for (String namespace : allKnownNamespaces) { + DeviceConfig.resetToDefaults(resetMode, namespace); + } + addToKnownResetNamespaces(allKnownNamespaces); + } + + /** + * Returns a list of all known DeviceConfig namespaces, except for the special {@link + * #NAMESPACE_RESCUE_PARTY} + */ + private static List<String> getAllKnownDeviceConfigNamespacesList() { + String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY, + ALL_KNOWN_NAMESPACES_FLAG); + List<String> namespacesList = toStringList(namespacesStr); + namespacesList.remove(NAMESPACE_RESCUE_PARTY); + return namespacesList; + } + + private static List<String> toStringList(String serialized) { + if (serialized == null || serialized.length() == 0) { + return new ArrayList<>(); + } + return Arrays.asList(serialized.split(DELIMITER)); + } + + private static int incrementAndRetrieveResetNamespacesFlagCounter() { + sKnownResetNamespacesFlagCounter++; + if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) { + sKnownResetNamespacesFlagCounter = 0; + } + return sKnownResetNamespacesFlagCounter; + } +} 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..087de69b6c12 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -87,8 +87,6 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; import static android.os.Process.SYSTEM_UID; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER; import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK; @@ -2677,8 +2675,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(); @@ -2827,28 +2826,6 @@ final class ActivityRecord extends ConfigurationContainer { outAppBounds.setEmpty(); } - // TODO(b/112288258): Remove below calculation because the position information in bounds - // will be replaced by the offset of surface. - final Rect appBounds = parentConfig.windowConfiguration.getAppBounds(); - if (appBounds != null) { - final Rect outBounds = inOutConfig.windowConfiguration.getBounds(); - final int activityWidth = outBounds.width(); - final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId()); - if (navBarPosition == NAV_BAR_LEFT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = appBounds.right - activityWidth; - outBounds.right = appBounds.right; - } else if (navBarPosition == NAV_BAR_RIGHT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = 0; - outBounds.right = activityWidth + appBounds.left; - } else if (appBounds.width() > activityWidth) { - // Horizontally center the frame. - outBounds.left = appBounds.left + (appBounds.width() - activityWidth) / 2; - outBounds.right = outBounds.left + activityWidth; - } - } - task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds); } @@ -2936,14 +2913,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/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 0a3c2fba3930..c685b05e99e0 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2332,6 +2332,20 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (!task.canBeLaunchedOnDisplay(actualDisplayId)) { throw new IllegalStateException("Task resolved to incompatible display"); } + + final ActivityDisplay preferredDisplay = + mRootActivityContainer.getActivityDisplay(preferredDisplayId); + + final boolean singleTaskInstance = preferredDisplay != null + && preferredDisplay.isSingleTaskInstance(); + + if (singleTaskInstance) { + // Suppress the warning toast if the preferredDisplay was set to singleTask. + // The singleTaskInstance displays will only contain one task and any attempt to + // launch new task will re-route to the default display. + return; + } + if (preferredDisplayId != actualDisplayId) { Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId); // Display a warning toast that we failed to put a task on a secondary display. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c33a2c179ab7..538813e9b21c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -98,6 +98,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; @@ -115,6 +116,7 @@ import android.util.Pools.SynchronizedPool; import android.util.Slog; import android.widget.Toast; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; @@ -761,11 +763,11 @@ class ActivityStarter { abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); // TODO: remove this toast after feature development is done if (abortBackgroundStart) { - final String toastMsg = abort - ? "Background activity start from " + callingPackage - + " blocked. See go/q-bg-block." - : "This background activity start from " + callingPackage - + " will be blocked in future Q builds. See go/q-bg-block."; + final Resources res = mService.mContext.getResources(); + final String toastMsg = res.getString(abort + ? R.string.activity_starter_block_bg_activity_starts_enforcing + : R.string.activity_starter_block_bg_activity_starts_permissive, + callingPackage); mService.mUiHandler.post(() -> { Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show(); }); @@ -830,12 +832,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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 258819fdece9..df73d1dbc7c6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2467,7 +2467,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final Rect destBounds = new Rect(); stack.getAnimationOrCurrentBounds(destBounds); - if (!destBounds.isEmpty() || !destBounds.equals(compareBounds)) { + if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) { Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); return; } @@ -3202,23 +3202,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void exitFreeformMode(IBinder token) { + public void toggleFreeformWindowingMode(IBinder token) { synchronized (mGlobalLock) { long ident = Binder.clearCallingIdentity(); try { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { throw new IllegalArgumentException( - "exitFreeformMode: No activity record matching token=" + token); + "toggleFreeformWindowingMode: No activity record matching token=" + + token); } final ActivityStack stack = r.getActivityStack(); - if (stack == null || !stack.inFreeformWindowingMode()) { - throw new IllegalStateException( - "exitFreeformMode: You can only go fullscreen from freeform."); + if (stack == null) { + throw new IllegalStateException("toggleFreeformWindowingMode: the activity " + + "doesn't have a stack"); + } + + if (!stack.inFreeformWindowingMode() + && stack.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + throw new IllegalStateException("toggleFreeformWindowingMode: You can only " + + "toggle between fullscreen and freeform."); } - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + if (stack.inFreeformWindowingMode()) { + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else { + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + } } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 6dc73bbb80cb..19ff43822923 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1879,6 +1879,9 @@ public class AppTransition implements Dump { mNextAppTransitionAnimationsSpecsFuture = specsFuture; mNextAppTransitionScaleUp = scaleUp; mNextAppTransitionFutureCallback = callback; + if (isReady()) { + fetchAppTransitionSpecsFromFuture(); + } } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 97062a6065e2..5c528c7f6047 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -568,7 +568,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { final long token = proto.start(fieldId); mRequestedOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION, - logLevel != WindowTraceLogLevel.CRITICAL); + logLevel == WindowTraceLogLevel.CRITICAL); if (logLevel == WindowTraceLogLevel.ALL) { mFullConfiguration.writeToProto(proto, FULL_CONFIGURATION, false /* critical */); mMergedOverrideConfiguration.writeToProto(proto, MERGED_OVERRIDE_CONFIGURATION, 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/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index ea65dd99077a..1d76a71aaea1 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -396,6 +396,10 @@ public class DockedStackDividerController { if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight) || mAdjustedForDivider != adjustedForDivider) { if (animate && !mAnimatingForMinimizedDockedStack) { + // Notify SystemUI to set the target docked stack size according current docked + // state without animation when calling startImeAdjustAnimation. + notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */, + isHomeStackResizable()); startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); } else { // Animation might be delayed, so only notify if we don't run an animation. @@ -889,7 +893,10 @@ public class DockedStackDividerController { } if (mAnimatingForMinimizedDockedStack) { return animateForMinimizedDockedStack(now); - } else if (mAnimatingForIme) { + } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) { + // To prevent task stack resize animation may flicking when playing app transition + // animation & IME window enter animation in parallel, make sure app transition is done + // and then start to animate for IME. return animateForIme(now); } return false; diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 5c91d9e6fff2..698835772df5 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1126,6 +1126,13 @@ class RootActivityContainer extends ConfigurationContainer if (!stack.isFocusableAndVisible() || topRunningActivity == null) { continue; } + if (stack == targetStack) { + // Simply update the result for targetStack because the targetStack had + // already resumed in above. We don't want to resume it again, especially in + // some cases, it would cause a second launch failure if app process was dead. + resumedOnDisplay |= result; + continue; + } if (topRunningActivity.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. stack.executeAppTransition(targetOptions); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 59d7560fc5b0..9d3112fec0f2 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -2134,7 +2134,7 @@ class TaskRecord extends ConfigurationContainer { if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); inOutConfig.screenHeightDp = insideParentBounds - ? Math.min(overrideScreenHeightDp, parentConfig.screenWidthDp) + ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) : overrideScreenHeightDp; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 7b742fd2e0f3..b91519903eb8 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1163,6 +1163,14 @@ public class TaskStack extends WindowContainer<Task> implements } private boolean adjustForIME(final WindowState imeWin) { + // To prevent task stack resize animation may flicking when playing app transition + // animation & IME window enter animation in parallel, we need to make sure app + // transition is done and then adjust task size for IME, skip the new adjusted frame when + // app transition is still running. + if (getDisplayContent().mAppTransition.isRunning()) { + return false; + } + final int dockedSide = getDockSide(); final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; if (imeWin == null || !dockedTopOrBottom) { 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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3430987f4736..21a557e6809e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2164,9 +2164,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // A modal window uses the whole compatibility bounds. flags |= FLAG_NOT_TOUCH_MODAL; mTmpRect.set(mAppToken.getResolvedOverrideBounds()); - // TODO(b/112288258): Remove the forced offset when the override bounds always - // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}). - mTmpRect.offsetTo(0, 0); } else { // Non-modal uses the application based frame. mTmpRect.set(mWindowFrames.mCompatFrame); 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/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 9be728bac532..ec7a78beb122 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -295,6 +295,12 @@ static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint feature } } +static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) { + bool retval = false; + getSuspendControl()->forceSuspend(&retval); + return retval; +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gPowerManagerServiceMethods[] = { @@ -303,6 +309,8 @@ static const JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeInit }, { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", (void*) nativeAcquireSuspendBlocker }, + { "nativeForceSuspend", "()Z", + (void*) nativeForceSuspend }, { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", (void*) nativeReleaseSuspendBlocker }, { "nativeSetInteractive", "(Z)V", diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING new file mode 100644 index 000000000000..ab85a6873cf6 --- /dev/null +++ b/services/devicepolicy/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "CtsDevicePolicyManagerTestCases" + } + ] +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9523202cbb0e..ae48dad7b8f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5181,7 +5181,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - ActiveAdmin ap = getActiveAdminForCallerLocked( + final ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; @@ -8388,7 +8388,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - Log.w(LOG_TAG, String.format("Package if %s (uid=%d, pid=%d) cannot access Device IDs", + Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs", packageName, uid, pid)); return false; } 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/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java index 8df08262c9fa..8df08262c9fa 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java index 2af6f2bee8ff..2af6f2bee8ff 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java index 73baf80a2c70..73baf80a2c70 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml deleted file mode 100644 index 0f4e8a3ca0c6..000000000000 --- a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<certificates> - <metadata> - <serial> - 1000 - </serial> - <creation-time> - 1515697631 - </creation-time> - <refresh-interval> - 2592000 - </refresh-interval> - <refresh-interval> - 2592000 - </refresh-interval> - <previous> - <serial> - 0 - </serial> - <hash> - 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - </hash> - </previous> - </metadata> - <endpoints> - <cert> - MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi - R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1 - NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW - YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu - tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl - 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21 - tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu - HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr - GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb - UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe - 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5 - 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ - hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa - 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4 - M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf - JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp - BuwwuQxvQDF4pmQd - </cert> - </endpoints> -</certificates> diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml index 3da012257d8f..3da012257d8f 100644 --- a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml +++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml 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/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index bd03a8d22643..04abeca1192e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -403,7 +403,8 @@ public class UserControllerTest { protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, - boolean sticky, int callingPid, int callingUid, int userId) { + boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { Log.i(TAG, "broadcastIntentLocked " + intent); mSentIntents.add(intent); return 0; 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/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java index bbcc41113f5a..9836c64ea5b5 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java @@ -47,7 +47,6 @@ public final class CertXmlTest { public void parse_succeeds() throws Exception { CertXml certXml = CertXml.parse(certXmlBytes); assertThat(certXml.getSerial()).isEqualTo(1000L); - assertThat(certXml.getRefreshInterval()).isEqualTo(2592000L); } @Test @@ -75,27 +74,22 @@ public final class CertXmlTest { } @Test - public void parse_throwsIfNoEndpointCert() throws Exception { - CertParsingException expected = - expectThrows( - CertParsingException.class, - () -> - CertXml.parse( - TestData.readTestFile( - "xml/invalid-cert-file-no-endpoint-cert.xml"))); - assertThat(expected.getMessage()).contains("at least one"); + public void parse_doesNotThrowIfNoRefreshInterval() throws Exception { + CertXml.parse( + TestData.readTestFile( + "xml/valid-cert-file-no-refresh-interval.xml")); } @Test - public void parse_throwsIfNoRefreshInterval() throws Exception { + public void parse_throwsIfNoEndpointCert() throws Exception { CertParsingException expected = expectThrows( CertParsingException.class, () -> CertXml.parse( TestData.readTestFile( - "xml/invalid-cert-file-no-refresh-interval.xml"))); - assertThat(expected.getMessage()).contains("exactly one"); + "xml/invalid-cert-file-no-endpoint-cert.xml"))); + assertThat(expected.getMessage()).contains("at least one"); } @Test @@ -111,19 +105,6 @@ public final class CertXmlTest { } @Test - public void parse_throwsIfTwoRefreshIntervals() throws Exception { - CertParsingException expected = - expectThrows( - CertParsingException.class, - () -> - CertXml.parse( - TestData.readTestFile( - "xml/invalid-cert-file-two-refresh-intervals" - + ".xml"))); - assertThat(expected.getMessage()).contains("exactly one"); - } - - @Test public void parse_throwsIfTwoSerials() throws Exception { CertParsingException expected = expectThrows( 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/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 48ab8d6698bd..0196279cbf56 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -568,7 +568,8 @@ public class DexManagerTests { } private PackageDynamicCode getPackageDynamicCodeInfo(TestData testData) { - return mDexManager.getDexLogger().getPackageDynamicCodeInfo(testData.getPackageName()); + return mDexManager.getDynamicCodeLogger() + .getPackageDynamicCodeInfo(testData.getPackageName()); } private void assertNoUseInfo(TestData testData) { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java index 6da202b93065..7992ba33c8da 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java @@ -53,7 +53,7 @@ import org.mockito.stubbing.Stubber; @RunWith(AndroidJUnit4.class) @SmallTest -public class DexLoggerTests { +public class DynamicCodeLoggerTests { private static final String OWNING_PACKAGE_NAME = "package.name"; private static final String VOLUME_UUID = "volUuid"; private static final String FILE_PATH = "/bar/foo.jar"; @@ -85,7 +85,7 @@ public class DexLoggerTests { @Mock IPackageManager mPM; @Mock Installer mInstaller; - private DexLogger mDexLogger; + private DynamicCodeLogger mDynamicCodeLogger; private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create(); private boolean mWriteTriggered = false; @@ -106,7 +106,7 @@ public class DexLoggerTests { }; // For test purposes capture log messages as well as sending to the event log. - mDexLogger = new DexLogger(mPM, mInstaller, packageDynamicCodeLoading) { + mDynamicCodeLogger = new DynamicCodeLogger(mPM, mInstaller, packageDynamicCodeLoading) { @Override void writeDclEvent(String subtag, int uid, String message) { super.writeDclEvent(subtag, uid, message); @@ -131,13 +131,13 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES)); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isFalse(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); } @@ -146,14 +146,14 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(EMPTY_BYTES)); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH); // File should be removed from the DCL list, since we can't hash it. assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test @@ -162,24 +162,24 @@ public class DexLoggerTests { doThrow(new InstallerException("Intentional failure for test"))); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH); // File should be removed from the DCL list, since we can't hash it. assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test public void testOneLoader_ownFile_unknownPath() { recordLoad(OWNING_PACKAGE_NAME, "other/path"); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test @@ -189,7 +189,7 @@ public class DexLoggerTests { setPackageUid(OWNING_PACKAGE_NAME, -1); recordLoad(OWNING_PACKAGE_NAME, filePath); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); } @@ -200,7 +200,7 @@ public class DexLoggerTests { setPackageUid("other.package.name", 1001); recordLoad("other.package.name", FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(1001); assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH); @@ -213,7 +213,7 @@ public class DexLoggerTests { setPackageUid("other.package.name", -1); recordLoad("other.package.name", FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isFalse(); @@ -224,14 +224,14 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES)); recordLoadNative(FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid) .containsEntry(OWNER_UID, EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isFalse(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); } @@ -247,7 +247,7 @@ public class DexLoggerTests { recordLoad("other.package.name1", otherDexPath); recordLoad("other.package.name2", FILE_PATH); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID); assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH); @@ -256,10 +256,10 @@ public class DexLoggerTests { assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); - // Check the DexLogger caching is working + // Check the DynamicCodeLogger caching is working verify(mPM, atMost(1)).getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID); } @@ -267,7 +267,7 @@ public class DexLoggerTests { public void testUnknownOwner() { reset(mPM); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading("other.package.name"); + mDynamicCodeLogger.logDynamicCodeLoading("other.package.name"); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isFalse(); @@ -278,11 +278,11 @@ public class DexLoggerTests { public void testUninstalledPackage() { reset(mPM); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } private void setPackageUid(String packageName, int uid) throws Exception { @@ -295,7 +295,8 @@ public class DexLoggerTests { } private void recordLoad(String loadingPackageName, String dexPath) { - mDexLogger.recordDex(OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName); + mDynamicCodeLogger.recordDex( + OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName); mWriteTriggered = false; } @@ -304,7 +305,7 @@ public class DexLoggerTests { String[] packageNames = { OWNING_PACKAGE_NAME }; when(mPM.getPackagesForUid(loadingUid)).thenReturn(packageNames); - mDexLogger.recordNative(loadingUid, nativePath); + mDynamicCodeLogger.recordNative(loadingUid, nativePath); mWriteTriggered = false; } } 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/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 63341b6ea38a..911c4a2f4122 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -21,15 +21,21 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; +import android.attention.AttentionManagerInternal; import android.content.Context; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.BatteryManagerInternal; +import android.os.Binder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; @@ -53,6 +59,9 @@ import org.junit.Rule; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.HashMap; +import java.util.Map; + /** * Tests for {@link com.android.server.power.PowerManagerService} */ @@ -67,6 +76,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { private @Mock DisplayManagerInternal mDisplayManagerInternalMock; private @Mock BatteryManagerInternal mBatteryManagerInternalMock; private @Mock ActivityManagerInternal mActivityManagerInternalMock; + private @Mock AttentionManagerInternal mAttentionManagerInternalMock; private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock; private @Mock Notifier mNotifierMock; private PowerManagerService mService; @@ -93,6 +103,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock); addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock); addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); mService = new PowerManagerService(getContext(), new Injector() { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, @@ -210,4 +221,80 @@ public class PowerManagerServiceTest extends AndroidTestCase { mService.onUserActivity(); assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse(); } + + @SmallTest + public void testForceSuspend_putsDeviceToSleep() { + mService.systemReady(null); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Verify that we start awake + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab the wakefulness value when PowerManager finally calls into the + // native component to actually perform the suspend. + when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> { + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + return true; + }); + + boolean retval = mService.getBinderServiceInstance().forceSuspend(); + assertThat(retval).isTrue(); + + // Still asleep when the function returns. + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @SmallTest + public void testForceSuspend_pakeLocksDisabled() { + final String tag = "TestWakelockTag_098213"; + final int flags = PowerManager.PARTIAL_WAKE_LOCK; + final String pkg = getContext().getOpPackageName(); + + // Set up the Notification mock to keep track of the wakelocks that are currently + // active or disabled. We'll use this to verify that wakelocks are disabled when + // they should be. + final Map<String, Integer> wakelockMap = new HashMap<>(1); + doAnswer(inv -> { + wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]); + return null; + }).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(), + anyInt(), any(), any()); + doAnswer(inv -> { + wakelockMap.remove((String) inv.getArguments()[1]); + return null; + }).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(), + anyInt(), any(), any()); + + // + // TEST STARTS HERE + // + mService.systemReady(null); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Verify that we start awake + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + + // Create a wakelock + mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg, + null /* workSource */, null /* historyTag */); + assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active. + + // Confirm that the wakelocks have been disabled when the forceSuspend is in flight. + when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> { + // Verify that the wakelock is disabled by the time we get to the native force + // suspend call. + assertThat(wakelockMap.containsKey(tag)).isFalse(); + return true; + }); + + assertThat(mService.getBinderServiceInstance().forceSuspend()).isTrue(); + assertThat(wakelockMap.get(tag)).isEqualTo(flags); + + } + + @SmallTest + public void testForceSuspend_forceSuspendFailurePropogated() { + when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); + assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 8c36905d8422..a1db3e8d8ded 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -27,9 +27,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; @@ -158,35 +155,6 @@ public class ActivityRecordTests extends ActivityTestsBase { assertTrue(mActivity.isState(STOPPED)); } - @Test - public void testPositionLimitedAspectRatioNavBarBottom() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f, - new Rect(0, 0, 1000, 1500)); - } - - @Test - public void testPositionLimitedAspectRatioNavBarLeft() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f, - new Rect(500, 0, 2000, 1000)); - } - - @Test - public void testPositionLimitedAspectRatioNavBarRight() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f, - new Rect(0, 0, 1500, 1000)); - } - - private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds, - float aspectRatio, Rect expectedActivityBounds) { - // Verify with nav bar on the right. - when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId())) - .thenReturn(navBarPosition); - mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds); - mActivity.info.maxAspectRatio = aspectRatio; - ensureActivityConfiguration(); - assertEquals(expectedActivityBounds, mActivity.getBounds()); - } - private void ensureActivityConfiguration() { mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } 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/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index a62bc713db40..2452ef0425e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -61,7 +61,6 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; -import android.view.DisplayInfo; import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; @@ -87,18 +86,22 @@ import java.util.List; * Tests for the {@link DisplayContent} class. * * Build/Install/Run: - * atest FrameworksServicesTests:DisplayContentTests + * atest WmTests:DisplayContentTests */ @SmallTest @Presubmit public class DisplayContentTests extends WindowTestsBase { @Test - @FlakyTest(bugId = 77772044) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testForAllWindows() { final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "exiting app"); final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; + // Wait until everything in animation handler get executed to prevent the exiting window + // from being removed during WindowSurfacePlacer Traversal. + waitUntilHandlersIdle(); + exitingAppToken.mIsExiting = true; exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken); 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/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index bcf9dd218835..388f98ffe4a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -24,6 +24,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -307,6 +308,41 @@ public class TaskRecordTests extends ActivityTestsBase { assertEquals(fullScreenBounds, task.getBounds()); } + @Test + public void testComputeConfigResourceOverrides() { + final TaskRecord task = new TaskBuilder(mSupervisor).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final int longSide = 1200; + final int shortSide = 600; + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px + parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px + + // Portrait bounds. + inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide); + // By default, the parent bounds should limit the existing input bounds. + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); + assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); + + inOutConfig.setToDefaults(); + // Landscape bounds. + inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide); + // Without limiting to be inside the parent bounds, the out screen size should keep relative + // to the input bounds. + task.computeConfigResourceOverrides(inOutConfig, parentConfig, + false /* insideParentBounds */); + + assertEquals(shortSide * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenHeightDp); + assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenWidthDp); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); + } + /** Ensures that the alias intent won't have target component resolved. */ @Test public void testTaskIntentActivityAlias() { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index dcca3167c752..42a205a2fb3d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -100,7 +100,7 @@ public class TaskStackChangedListenerTest { } @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testTaskDescriptionChanged() throws Exception { final Object[] params = new Object[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -122,14 +122,18 @@ public class TaskStackChangedListenerTest { } } }); - final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class); + + int taskId; + synchronized (sLock) { + taskId = startTestActivity(ActivityTaskDescriptionChange.class).getTaskId(); + } waitForCallback(latch); - assertEquals(activity.getTaskId(), params[0]); + assertEquals(taskId, params[0]); assertEquals("Test Label", ((TaskDescription) params[1]).getLabel()); } @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testActivityRequestedOrientationChanged() throws Exception { final int[] params = new int[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -142,9 +146,12 @@ public class TaskStackChangedListenerTest { latch.countDown(); } }); - final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class); + int taskId; + synchronized (sLock) { + taskId = startTestActivity(ActivityRequestedOrientationChange.class).getTaskId(); + } waitForCallback(latch); - assertEquals(activity.getTaskId(), params[0]); + assertEquals(taskId, params[0]); assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]); } @@ -152,7 +159,7 @@ public class TaskStackChangedListenerTest { * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted. */ @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testTaskChangeCallBacks() throws Exception { final Object[] params = new Object[2]; final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1); @@ -245,7 +252,7 @@ public class TaskStackChangedListenerTest { private void waitForCallback(CountDownLatch latch) { try { - final boolean result = latch.await(2, TimeUnit.SECONDS); + final boolean result = latch.await(4, TimeUnit.SECONDS); if (!result) { throw new RuntimeException("Timed out waiting for task stack change notification"); } @@ -324,7 +331,11 @@ public class TaskStackChangedListenerTest { protected void onPostResume() { super.onPostResume(); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - finish(); + synchronized (sLock) { + // Hold the lock to ensure no one is trying to access fields of this Activity in + // this test. + finish(); + } } } @@ -333,7 +344,11 @@ public class TaskStackChangedListenerTest { protected void onPostResume() { super.onPostResume(); setTaskDescription(new TaskDescription("Test Label")); - finish(); + synchronized (sLock) { + // Hold the lock to ensure no one is trying to access fields of this Activity in + // this test. + finish(); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 3eb9085b68f6..b0e20b89b811 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -54,7 +55,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; import android.graphics.Insets; import android.graphics.Matrix; @@ -87,6 +87,7 @@ import java.util.LinkedList; */ @SmallTest @Presubmit +@FlakyTest(bugId = 124127512) public class WindowStateTests extends WindowTestsBase { private static int sPreviousNewInsetsMode; @@ -389,6 +390,7 @@ public class WindowStateTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 74078662) public void testLayoutSeqResetOnReparent() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); app.mLayoutSeq = 1; @@ -445,6 +447,7 @@ public class WindowStateTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 74078662) public void testDisplayCutoutIsCalculatedRelativeToFrame() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); WindowFrames wf = app.getWindowFrames(); 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/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 0eb991777d74..16791a4b8680 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -18,7 +18,6 @@ package android.telecom; import android.content.Context; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.telecom.Logging.EventManager; import android.telecom.Logging.Session; @@ -29,8 +28,6 @@ import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.IllegalFormatException; import java.util.Locale; @@ -373,6 +370,12 @@ public class Log { return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level); } + /** + * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone + * phone number in {@link String} format. + * @param pii The information to obfuscate. + * @return The obfuscated string. + */ public static String piiHandle(Object pii) { if (pii == null || VERBOSE) { return String.valueOf(pii); @@ -389,16 +392,7 @@ public class Log { String textToObfuscate = uri.getSchemeSpecificPart(); if (PhoneAccount.SCHEME_TEL.equals(scheme)) { - int numDigitsToObfuscate = getDialableCount(textToObfuscate) - - NUM_DIALABLE_DIGITS_TO_LOG; - for (int i = 0; i < textToObfuscate.length(); i++) { - char c = textToObfuscate.charAt(i); - boolean isDialable = PhoneNumberUtils.isDialable(c); - if (isDialable) { - numDigitsToObfuscate--; - } - sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c); - } + obfuscatePhoneNumber(sb, textToObfuscate); } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) { for (int i = 0; i < textToObfuscate.length(); i++) { char c = textToObfuscate.charAt(i); @@ -410,12 +404,34 @@ public class Log { } else { sb.append(pii(pii)); } + } else if (pii instanceof String) { + String number = (String) pii; + obfuscatePhoneNumber(sb, number); } return sb.toString(); } /** + * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the + * phone number. + * @param sb String buffer to write obfuscated number to. + * @param phoneNumber The number to obfuscate. + */ + private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) { + int numDigitsToObfuscate = getDialableCount(phoneNumber) + - NUM_DIALABLE_DIGITS_TO_LOG; + for (int i = 0; i < phoneNumber.length(); i++) { + char c = phoneNumber.charAt(i); + boolean isDialable = PhoneNumberUtils.isDialable(c); + if (isDialable) { + numDigitsToObfuscate--; + } + sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c); + } + } + + /** * Determines the number of dialable characters in a string. * @param toCount The string to count dialable characters in. * @return The count of dialable characters. diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index e99a289729a4..d5091680285b 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -291,6 +291,19 @@ public class TelecomManager { "android.telecom.extra.OUTGOING_CALL_EXTRAS"; /** + * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell + * whether the user's dial intent is emergency; this is required to specify when the dialed + * number is ambiguous, identified as both emergency number and any other non-emergency number; + * e.g. in some situation, 611 could be both an emergency number in a country and a + * non-emergency number of a carrier's customer service hotline. + * + * @hide + */ + @SystemApi + public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = + "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL"; + + /** * @hide */ public static final String EXTRA_UNKNOWN_CALL_HANDLE = 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/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 38143335dbf1..dba437a3a007 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -141,6 +141,14 @@ public final class CellIdentityTdscdma extends CellIdentity { return mCpid; } + /** + * @return 16-bit UMTS Absolute RF Channel Number, + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + */ + public int getUarfcn() { + return mUarfcn; + } + /** @hide */ @Override public int getChannelNumber() { diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java index 53d69f447a56..d98f37d976df 100644 --- a/telephony/java/android/telephony/LocationAccessPolicy.java +++ b/telephony/java/android/telephony/LocationAccessPolicy.java @@ -26,11 +26,12 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.location.LocationManager; import android.os.Binder; +import android.os.Build; import android.os.Process; -import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import android.widget.Toast; import java.util.List; @@ -41,61 +42,236 @@ import java.util.List; public final class LocationAccessPolicy { private static final String TAG = "LocationAccessPolicy"; private static final boolean DBG = false; + public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.CUR_DEVELOPMENT; - /** - * API to determine if the caller has permissions to get cell location. - * - * @param pkgName Package name of the application requesting access - * @param uid The uid of the package - * @param pid The pid of the package - * @param throwOnDeniedPermission Whether to throw if the location permission is denied. - * @return boolean true or false if permissions is granted - */ - public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName, - int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException { - Trace.beginSection("TelephonyLocationCheck"); - try { - // Always allow the phone process and system server to access location. This avoid - // breaking legacy code that rely on public-facing APIs to access cell location, and - // it doesn't create an info leak risk because the cell location is stored in the phone - // process anyway, and the system server already has location access. - if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { - return true; + public enum LocationPermissionResult { + ALLOWED, + /** + * Indicates that the denial is due to a transient device state + * (e.g. app-ops, location master switch) + */ + DENIED_SOFT, + /** + * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest) + */ + DENIED_HARD, + } + + public static class LocationPermissionQuery { + public final String callingPackage; + public final int callingUid; + public final int callingPid; + public final int minSdkVersionForCoarse; + public final int minSdkVersionForFine; + public final String method; + + private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid, + int minSdkVersionForCoarse, int minSdkVersionForFine, String method) { + this.callingPackage = callingPackage; + this.callingUid = callingUid; + this.callingPid = callingPid; + this.minSdkVersionForCoarse = minSdkVersionForCoarse; + this.minSdkVersionForFine = minSdkVersionForFine; + this.method = method; + } + + public static class Builder { + private String mCallingPackage; + private int mCallingUid; + private int mCallingPid; + private int mMinSdkVersionForCoarse = Integer.MAX_VALUE; + private int mMinSdkVersionForFine = Integer.MAX_VALUE; + private String mMethod; + + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingPackage(String callingPackage) { + mCallingPackage = callingPackage; + return this; } - // We always require the location permission and also require the - // location mode to be on for non-legacy apps. Legacy apps are - // required to be in the foreground to at least mitigate the case - // where a legacy app the user is not using tracks their location. - // Granting ACCESS_FINE_LOCATION to an app automatically grants it - // ACCESS_COARSE_LOCATION. - if (throwOnDeniedPermission) { - context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION, - pid, uid, "canAccessCellLocation"); - } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, - pid, uid) == PackageManager.PERMISSION_DENIED) { - if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")"); - return false; + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingUid(int callingUid) { + mCallingUid = callingUid; + return this; } - final int opCode = AppOpsManager.permissionToOpCode( - Manifest.permission.ACCESS_COARSE_LOCATION); - if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class) - .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) { - if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")"); - return false; + + /** + * Mandatory parameter, used for performing permission checks. + */ + public Builder setCallingPid(int callingPid) { + mCallingPid = callingPid; + return this; } - if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) { - if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); - return false; + + /** + * Apps that target at least this sdk version will be checked for coarse location + * permission. Defaults to INT_MAX (which means don't check) + */ + public Builder setMinSdkVersionForCoarse( + int minSdkVersionForCoarse) { + mMinSdkVersionForCoarse = minSdkVersionForCoarse; + return this; } - // If the user or profile is current, permission is granted. - // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. - return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context); - } finally { - Trace.endSection(); + + /** + * Apps that target at least this sdk version will be checked for fine location + * permission. Defaults to INT_MAX (which means don't check) + */ + public Builder setMinSdkVersionForFine( + int minSdkVersionForFine) { + mMinSdkVersionForFine = minSdkVersionForFine; + return this; + } + + /** + * Optional, for logging purposes only. + */ + public Builder setMethod(String method) { + mMethod = method; + return this; + } + + public LocationPermissionQuery build() { + return new LocationPermissionQuery(mCallingPackage, mCallingUid, + mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod); + } + } + } + + private static void logError(Context context, String errorMsg) { + Log.e(TAG, errorMsg); + try { + if (Build.IS_DEBUGGABLE) { + Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show(); + } + } catch (Throwable t) { + // whatever, not important + } + } + + private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) { + switch (appOpsMode) { + case AppOpsManager.MODE_ALLOWED: + return LocationPermissionResult.ALLOWED; + case AppOpsManager.MODE_ERRORED: + return LocationPermissionResult.DENIED_HARD; + default: + return LocationPermissionResult.DENIED_SOFT; } } + private static LocationPermissionResult checkAppLocationPermissionHelper(Context context, + LocationPermissionQuery query, String permissionToCheck) { + String locationTypeForLog = + Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck) + ? "fine" : "coarse"; + + // Do the app-ops and the manifest check without any of the allow-overrides first. + boolean hasManifestPermission = checkManifestPermission(context, query.callingPid, + query.callingUid, permissionToCheck); + + int appOpMode = context.getSystemService(AppOpsManager.class) + .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck), + query.callingUid, query.callingPackage); + + if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) { + // If the app did everything right, return without logging. + return LocationPermissionResult.ALLOWED; + } + + // If the app has the manifest permission but not the app-op permission, it means that + // it's aware of the requirement and the user denied permission explicitly. If we see + // this, don't let any of the overrides happen. + if (hasManifestPermission) { + Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the" + + " app-ops permission is specifically denied."); + return appOpsModeToPermissionResult(appOpMode); + } + + int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck) + ? query.minSdkVersionForFine : query.minSdkVersionForCoarse; + + // If the app fails for some reason, see if it should be allowed to proceed. + if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) { + String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog + + " because we're not enforcing API " + minSdkVersion + " yet." + + " Please fix this app because it will break in the future. Called from " + + query.method; + logError(context, errorMsg); + return null; + } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) { + String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog + + " because it doesn't target API " + minSdkVersion + " yet." + + " Please fix this app. Called from " + query.method; + logError(context, errorMsg); + return null; + } else { + // If we're not allowing it due to the above two conditions, this means that the app + // did not declare the permission in their manifest. + return LocationPermissionResult.DENIED_HARD; + } + } + + public static LocationPermissionResult checkLocationPermission( + Context context, LocationPermissionQuery query) { + // Always allow the phone process and system server to access location. This avoid + // breaking legacy code that rely on public-facing APIs to access cell location, and + // it doesn't create an info leak risk because the cell location is stored in the phone + // process anyway, and the system server already has location access. + if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID + || query.callingUid == Process.ROOT_UID) { + return LocationPermissionResult.ALLOWED; + } + + // Check the system-wide requirements. If the location master switch is off or + // the app's profile isn't in foreground, return a soft denial. + if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) { + return LocationPermissionResult.DENIED_SOFT; + } + + // Do the check for fine, then for coarse. + if (query.minSdkVersionForFine < Integer.MAX_VALUE) { + LocationPermissionResult resultForFine = checkAppLocationPermissionHelper( + context, query, Manifest.permission.ACCESS_FINE_LOCATION); + if (resultForFine != null) { + return resultForFine; + } + } + + if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) { + LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper( + context, query, Manifest.permission.ACCESS_COARSE_LOCATION); + if (resultForCoarse != null) { + return resultForCoarse; + } + } + + // At this point, we're out of location checks to do. If the app bypassed all the previous + // ones due to the SDK grandfathering schemes, allow it access. + return LocationPermissionResult.ALLOWED; + } + + + private static boolean checkManifestPermission(Context context, int pid, int uid, + String permissionToCheck) { + return context.checkPermission(permissionToCheck, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } + + private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) { + if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) { + if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); + return false; + } + // If the user or profile is current, permission is granted. + // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. + return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid); + } + private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { LocationManager locationManager = context.getSystemService(LocationManager.class); if (locationManager == null) { @@ -105,10 +281,10 @@ public final class LocationAccessPolicy { return locationManager.isLocationEnabledForUser(UserHandle.of(userId)); } - private static boolean checkInteractAcrossUsersFull(@NonNull Context context) { - return context.checkCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - == PackageManager.PERMISSION_GRANTED; + private static boolean checkInteractAcrossUsersFull( + @NonNull Context context, int pid, int uid) { + return checkManifestPermission(context, pid, uid, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); } private static boolean isCurrentProfile(@NonNull Context context, int uid) { @@ -132,4 +308,18 @@ public final class LocationAccessPolicy { Binder.restoreCallingIdentity(token); } } -} + + private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) { + try { + if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion + >= sdkVersion) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // In case of exception, assume known app (more strict checking) + // Note: This case will never happen since checkPackage is + // called to verify validity before checking app's version. + } + return false; + } +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index c37b492a9cf1..6e6d59e62148 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -152,7 +152,7 @@ public class NetworkRegistrationState implements Parcelable { private final int[] mAvailableServices; @Nullable - private final CellIdentity mCellIdentity; + private CellIdentity mCellIdentity; @Nullable private VoiceSpecificRegistrationStates mVoiceSpecificStates; @@ -521,4 +521,22 @@ public class NetworkRegistrationState implements Parcelable { return new NetworkRegistrationState[size]; } }; + + /** + * @hide + */ + public NetworkRegistrationState sanitizeLocationInfo() { + NetworkRegistrationState result = copy(); + result.mCellIdentity = null; + return result; + } + + private NetworkRegistrationState copy() { + Parcel p = Parcel.obtain(); + this.writeToParcel(p, 0); + p.setDataPosition(0); + NetworkRegistrationState result = new NetworkRegistrationState(p); + p.recycle(); + return result; + } } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 2c9ba1dfff7b..ffebc04c39e6 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -46,7 +46,7 @@ import java.util.concurrent.Executor; * Override the methods for the state that you wish to receive updates for, and * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are - * called when the state changes, os well as once on initial registration. + * called when the state changes, as well as once on initial registration. * <p> * Note that access to some telephony information is * permission-protected. Your application won't receive updates for protected @@ -297,14 +297,17 @@ public class PhoneStateListener { public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; /** - * Listen for changes to preferred data subId. - * See {@link SubscriptionManager#setPreferredDataSubId(int)} - * for more details. + * Listen for changes to active data subId. Active data subscription + * is whichever is being used for Internet data. For most of the case, it's + * default data subscription but it could be others. For example, when data is + * switched to opportunistic subscription, that becomes the active data sub. * - * @see #onPreferredDataSubIdChanged + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} + * @see #onActiveDataSubIdChanged * @hide */ - public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE = 0x00400000; + public static final int LISTEN_ACTIVE_DATA_SUBID_CHANGE = 0x00400000; /** * Listen for changes to the radio power state. @@ -710,14 +713,14 @@ public class PhoneStateListener { } /** - * Callback invoked when preferred data subId changes. Requires - * the READ_PRIVILEGED_PHONE_STATE permission. - * @param subId the new preferred data subId. If it's INVALID_SUBSCRIPTION_ID, - * it means it's unset and defaultDataSub is used to determine which - * modem is preferred. + * Callback invoked when active data subId changes. Requires + * the READ_PHONE_STATE permission. + * @param subId current data subId used for Internet data. It will be default data subscription + * most cases. And it could be other subscriptions for example opportunistic + * subscription if data is switched onto it. * @hide */ - public void onPreferredDataSubIdChanged(int subId) { + public void onActiveDataSubIdChanged(int subId) { // default implementation empty } @@ -1001,12 +1004,12 @@ public class PhoneStateListener { () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes))); } - public void onPreferredDataSubIdChanged(int subId) { + public void onActiveDataSubIdChanged(int subId) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> psl.onPreferredDataSubIdChanged(subId))); + () -> mExecutor.execute(() -> psl.onActiveDataSubIdChanged(subId))); } public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) { diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 402763e52cfa..a1aee6d8217f 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * Contains phone state and service related information. @@ -1885,4 +1886,29 @@ public class ServiceState implements Parcelable { ? range1 : range2; } + + /** + * Returns a copy of self with location-identifying information removed. + * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation + * is true, clears other info as well. + * @hide + */ + public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) { + ServiceState state = new ServiceState(this); + if (state.mNetworkRegistrationStates != null) { + state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream() + .map(NetworkRegistrationState::sanitizeLocationInfo) + .collect(Collectors.toList()); + } + if (!removeCoarseLocation) return state; + + state.mDataOperatorAlphaLong = null; + state.mDataOperatorAlphaShort = null; + state.mDataOperatorNumeric = null; + state.mVoiceOperatorAlphaLong = null; + state.mVoiceOperatorAlphaShort = null; + state.mVoiceOperatorNumeric = null; + + return state; + } } 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 3a4d33c18485..94f26a8a8d61 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -866,7 +866,8 @@ public class SubscriptionManager { } /** - * Callback invoked when there is any change to any SubscriptionInfo. Typically + * Callback invoked when there is any change to any SubscriptionInfo, as well as once on + * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically * this method would invoke {@link #getActiveSubscriptionInfoList} */ public void onSubscriptionsChanged() { @@ -918,7 +919,9 @@ public class SubscriptionManager { /** * Register for changes to the list of active {@link SubscriptionInfo} records or to the * individual records themselves. When a change occurs the onSubscriptionsChanged method of - * the listener will be invoked immediately if there has been a notification. + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. * * @param listener an instance of {@link OnSubscriptionsChangedListener} with * onSubscriptionsChanged overridden. @@ -1233,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); } /** @@ -1861,7 +1864,7 @@ public class SubscriptionManager { iSub.setDefaultSmsSubId(subscriptionId); } } catch (RemoteException ex) { - // ignore it + ex.rethrowFromSystemServer(); } } @@ -2855,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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6690bd0f10b5..ced4f4ab6573 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -229,10 +229,19 @@ public class TelephonyManager { public static final int SRVCC_STATE_HANDOVER_CANCELED = 3; /** - * An invalid UICC card identifier. See {@link #getCardIdForDefaultEuicc()} and - * {@link UiccCardInfo#getCardId()}. + * A UICC card identifier used if the device does not support the operation. + * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no + * eUICC, or the eUICC cannot be read. */ - public static final int INVALID_CARD_ID = -1; + public static final int UNSUPPORTED_CARD_ID = -1; + + /** + * A UICC card identifier used before the UICC card is loaded. See + * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}. + * <p> + * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}. + */ + public static final int UNINITIALIZED_CARD_ID = -2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -1689,10 +1698,7 @@ public class TelephonyManager { * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API. */ @Deprecated - @RequiresPermission(anyOf = { - android.Manifest.permission.ACCESS_COARSE_LOCATION, - android.Manifest.permission.ACCESS_FINE_LOCATION - }) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public CellLocation getCellLocation() { try { ITelephony telephony = getITelephony(); @@ -3178,24 +3184,25 @@ public class TelephonyManager { } /** - * Get the card ID of the default eUICC card. If there is no eUICC, returns - * {@link #INVALID_CARD_ID}. + * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns + * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs + * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}. * * <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are * unique to a device, and always refer to the same UICC or eUICC card unless the device goes * through a factory reset. * - * @return card ID of the default eUICC card. + * @return card ID of the default eUICC card, if loaded. */ public int getCardIdForDefaultEuicc() { try { ITelephony telephony = getITelephony(); if (telephony == null) { - return INVALID_CARD_ID; + return UNINITIALIZED_CARD_ID; } return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName()); } catch (RemoteException e) { - return INVALID_CARD_ID; + return UNINITIALIZED_CARD_ID; } } @@ -4941,7 +4948,7 @@ public class TelephonyManager { * @return List of {@link android.telephony.CellInfo}; null if cell * information is unavailable. */ - @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public List<CellInfo> getAllCellInfo() { try { ITelephony telephony = getITelephony(); @@ -5018,7 +5025,7 @@ public class TelephonyManager { * @param executor the executor on which callback will be invoked. * @param callback a callback to receive CellInfo. */ - @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate( @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { @@ -5057,7 +5064,7 @@ public class TelephonyManager { * @hide */ @SystemApi - @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION, + @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull WorkSource workSource, @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { @@ -6647,9 +6654,10 @@ public class TelephonyManager { * * <p> Note that this scan can take a long time (sometimes minutes) to happen. * - * <p>Requires Permission: + * <p>Requires Permissions: * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier * privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * * @return {@link CellNetworkScanResult} with the status * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of @@ -6658,12 +6666,15 @@ public class TelephonyManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) public CellNetworkScanResult getAvailableNetworks() { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getCellNetworkScanResults(getSubId()); + return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName()); } } catch (RemoteException ex) { Rlog.e(TAG, "getAvailableNetworks RemoteException", ex); @@ -6682,7 +6693,8 @@ public class TelephonyManager { * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling - * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. * * @param request Contains all the RAT with bands/channels that need to be scanned. * @param executor The executor through which the callback should be invoked. Since the scan @@ -6693,7 +6705,10 @@ public class TelephonyManager { * @return A NetworkScan obj which contains a callback which can be used to stop the scan. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) public NetworkScan requestNetworkScan( NetworkScanRequest request, Executor executor, TelephonyScanManager.NetworkScanCallback callback) { @@ -6702,7 +6717,8 @@ public class TelephonyManager { mTelephonyScanManager = new TelephonyScanManager(); } } - return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback); + return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback, + getOpPackageName()); } /** @@ -6712,7 +6728,10 @@ public class TelephonyManager { * @removed */ @Deprecated - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) public NetworkScan requestNetworkScan( NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) { return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback); @@ -8713,10 +8732,14 @@ public class TelephonyManager { * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) public ServiceState getServiceState() { return getServiceStateForSubscriber(getSubId()); } diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java index 96ff33255b53..91f74b867fa0 100644 --- a/telephony/java/android/telephony/TelephonyScanManager.java +++ b/telephony/java/android/telephony/TelephonyScanManager.java @@ -29,14 +29,14 @@ import android.os.Messenger; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; -import android.util.Log; import android.util.SparseArray; + +import com.android.internal.telephony.ITelephony; + import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; -import com.android.internal.telephony.ITelephony; - /** * Manages the radio access network scan requests and callbacks. */ @@ -183,6 +183,7 @@ public final class TelephonyScanManager { * * <p> * Requires Permission: + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} * Or the calling app has carrier privileges. @see #hasCarrierPrivileges * @@ -192,11 +193,13 @@ public final class TelephonyScanManager { * @hide */ public NetworkScan requestNetworkScan(int subId, - NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { + NetworkScanRequest request, Executor executor, NetworkScanCallback callback, + String callingPackage) { try { ITelephony telephony = getITelephony(); if (telephony != null) { - int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder()); + int scanId = telephony.requestNetworkScan( + subId, request, mMessenger, new Binder(), callingPackage); saveScanInfo(scanId, request, executor, callback); return new NetworkScan(scanId, subId); } diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 1bbf9f4c4d25..ad343498f9e1 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -460,7 +460,7 @@ public class EuiccManager { */ @Nullable public String getEid() { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { return null; } try { @@ -483,7 +483,7 @@ public class EuiccManager { @SystemApi @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus() { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { return EUICC_OTA_STATUS_UNAVAILABLE; } try { @@ -518,7 +518,7 @@ public class EuiccManager { @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void downloadSubscription(DownloadableSubscription subscription, boolean switchAfterDownload, PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -580,7 +580,7 @@ public class EuiccManager { @SystemApi @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { PendingIntent callbackIntent = resolutionIntent.getParcelableExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT); @@ -617,7 +617,7 @@ public class EuiccManager { @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata( DownloadableSubscription subscription, PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -647,7 +647,7 @@ public class EuiccManager { @SystemApi @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -666,7 +666,7 @@ public class EuiccManager { */ @Nullable public EuiccInfo getEuiccInfo() { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { return null; } try { @@ -691,7 +691,7 @@ public class EuiccManager { */ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -731,7 +731,7 @@ public class EuiccManager { */ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -757,7 +757,7 @@ public class EuiccManager { @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void updateSubscriptionNickname( int subscriptionId, String nickname, PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -781,7 +781,7 @@ public class EuiccManager { @SystemApi @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -811,7 +811,7 @@ public class EuiccManager { * @hide */ public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) { - if (!refreshCardIdIfInvalid()) { + if (!refreshCardIdIfUninitialized()) { sendUnavailableError(callbackIntent); return; } @@ -822,16 +822,24 @@ public class EuiccManager { } } - private boolean refreshCardIdIfInvalid() { - if (!isEnabled()) { - return false; - } - // Refresh mCardId if it's invalid. - if (mCardId == TelephonyManager.INVALID_CARD_ID) { + /** + * Refreshes the cardId if its uninitialized, and returns whether we should continue the + * operation. + * <p> + * Note that after a successful refresh, the mCardId may be TelephonyManager.UNSUPPORTED_CARD_ID + * on older HALs. For backwards compatability, we continue to the LPA and let it decide which + * card to use. + */ + private boolean refreshCardIdIfUninitialized() { + // Refresh mCardId if its UNINITIALIZED_CARD_ID + if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) { TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); mCardId = tm.getCardIdForDefaultEuicc(); } + if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) { + return false; + } return true; } diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 59167b7d5bba..47c8de52820e 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"; @@ -350,6 +355,9 @@ public final class ImsCallProfile implements Parcelable { /** Indicates if the call is for testing purpose */ private boolean mEmergencyCallTesting = false; + /** Indicates if we have known the intent of the user for the call is emergency */ + private boolean mHasKnownUserIntentEmergency = false; + /** * Extras associated with this {@link ImsCallProfile}. * <p> @@ -540,7 +548,8 @@ public final class ImsCallProfile implements Parcelable { + ", emergencyServiceCategories=" + mEmergencyServiceCategories + ", emergencyUrns=" + mEmergencyUrns + ", emergencyCallRouting=" + mEmergencyCallRouting - + ", emergencyCallTesting=" + mEmergencyCallTesting + " }"; + + ", emergencyCallTesting=" + mEmergencyCallTesting + + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency + " }"; } @Override @@ -559,6 +568,7 @@ public final class ImsCallProfile implements Parcelable { out.writeStringList(mEmergencyUrns); out.writeInt(mEmergencyCallRouting); out.writeBoolean(mEmergencyCallTesting); + out.writeBoolean(mHasKnownUserIntentEmergency); } private void readFromParcel(Parcel in) { @@ -570,6 +580,7 @@ public final class ImsCallProfile implements Parcelable { mEmergencyUrns = in.createStringArrayList(); mEmergencyCallRouting = in.readInt(); mEmergencyCallTesting = in.readBoolean(); + mHasKnownUserIntentEmergency = in.readBoolean(); } public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() { @@ -789,12 +800,13 @@ public final class ImsCallProfile implements Parcelable { * * @hide */ - public void setEmergencyCallInfo(EmergencyNumber num) { + public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) { setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial()); setEmergencyUrns(num.getEmergencyUrns()); setEmergencyCallRouting(num.getEmergencyCallRouting()); setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask() == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST); + setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency); } /** @@ -860,6 +872,19 @@ public final class ImsCallProfile implements Parcelable { } /** + * Set if we have known the user intent of the call is emergency. + * + * This is only used to specify when the dialed number is ambiguous when it can be identified + * as both emergency number and any other non-emergency number; e.g. in some situation, 611 + * could be both an emergency number in a country and a non-emergency number of a carrier's + * customer service hotline. + */ + @VisibleForTesting + public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) { + mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency; + } + + /** * Get the emergency service categories, only valid if {@link #getServiceType} returns * {@link #SERVICE_TYPE_EMERGENCY} * @@ -916,4 +941,16 @@ public final class ImsCallProfile implements Parcelable { public boolean isEmergencyCallTesting() { return mEmergencyCallTesting; } + + /** + * Checks if we have known the user intent of the call is emergency. + * + * This is only used to specify when the dialed number is ambiguous when it can be identified + * as both emergency number and any other non-emergency number; e.g. in some situation, 611 + * could be both an emergency number in a country and a non-emergency number of a carrier's + * customer service hotline. + */ + public boolean hasKnownUserIntentEmergency() { + return mHasKnownUserIntentEmergency; + } } diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 322ce45797ec..4a263f060ca5 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -54,7 +54,7 @@ oneway interface IPhoneStateListener { void onCarrierNetworkChange(in boolean active); void onUserMobileDataStateChanged(in boolean enabled); void onPhoneCapabilityChanged(in PhoneCapability capability); - void onPreferredDataSubIdChanged(in int subId); + void onActiveDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); void onCallAttributesChanged(in CallAttributes callAttributes); void onEmergencyNumberListChanged(in Map emergencyNumberList); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index caa367fbaafe..7089ee5671c2 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -765,7 +765,7 @@ interface ITelephony { * @param subId the id of the subscription. * @return CellNetworkScanResult containing status of scan and networks. */ - CellNetworkScanResult getCellNetworkScanResults(int subId); + CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage); /** * Perform a radio network scan and return the id of this scan. @@ -774,10 +774,11 @@ interface ITelephony { * @param request Defines all the configs for network scan. * @param messenger Callback messages will be sent using this messenger. * @param binder the binder object instantiated in TelephonyManager. + * @param callingPackage the calling package * @return An id for this scan. */ int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger, - in IBinder binder); + in IBinder binder, in String callingPackage); /** * Stop an existing radio network scan. diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index e9eba324acb0..a922ab601442 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -81,7 +81,7 @@ interface ITelephonyRegistry { void notifyCarrierNetworkChange(in boolean active); void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state); void notifyPhoneCapabilityChanged(in PhoneCapability capability); - void notifyPreferredDataSubIdChanged(int preferredSubId); + void notifyActiveDataSubIdChanged(int activeDataSubId); void notifyRadioPowerStateChanged(in int state); void notifyEmergencyNumberList(); void notifyCallQualityChanged(in CallQuality callQuality, int phoneId); 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/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index d5549cc1355e..07b3a97817a3 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -439,6 +439,14 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Require unknown permission").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + final Intent intent = new Intent(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 5038); + sendOrderedBroadcast(intent, "com.google.android.test.activity.permission.UNDEFINED"); + return true; + } + }); menu.add("Stack Doc").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { ActivityManager.AppTask task = findDocTask(); diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java index e92cc56322eb..99d1f5ff4bb3 100644 --- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java +++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java @@ -49,13 +49,13 @@ import java.util.List; import java.util.concurrent.TimeUnit; /** - * Integration tests for {@link com.android.server.pm.dex.DexLogger}. + * Integration tests for {@link DynamicCodeLogger}, formerly known as {@code DexLogger}. * * The setup for the test dynamically loads code in a jar extracted * from our assets (a secondary dex file). * * We then use shell commands to trigger dynamic code logging (and wait - * for it to complete). This causes DexLogger to log the hash of the + * for it to complete). This causes DynamicCodeLogger to log the hash of the * file's name and content. We verify that this message appears in * the event log. * @@ -65,6 +65,8 @@ import java.util.concurrent.TimeUnit; @RunWith(JUnit4.class) public final class DexLoggerIntegrationTests { + private static final String SHA_256 = "SHA-256"; + // Event log tag used for SNET related events private static final int SNET_TAG = 0x534e4554; @@ -76,6 +78,13 @@ public final class DexLoggerIntegrationTests { private static final int IDLE_LOGGING_JOB_ID = 2030028; private static final int AUDIT_WATCHING_JOB_ID = 203142925; + // For tests that rely on parsing audit logs, how often to retry. (There are many reasons why + // we might not see the audit logs, including throttling and delays in log generation, so to + // avoid flakiness we run these tests multiple times, allowing progressively longer between + // code loading and checking the logs on each try.) + private static final int AUDIT_LOG_RETRIES = 10; + private static final int RETRY_DELAY_MS = 2_000; + private static Context sContext; private static int sMyUid; @@ -99,7 +108,7 @@ public final class DexLoggerIntegrationTests { } @Test - public void testDexLoggerGeneratesEvents_standardClassLoader() throws Exception { + public void testGeneratesEvents_standardClassLoader() throws Exception { File privateCopyFile = privateFile("copied.jar"); // Obtained via "echo -n copied.jar | sha256sum" String expectedNameHash = @@ -121,7 +130,7 @@ public final class DexLoggerIntegrationTests { } @Test - public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception { + public void testGeneratesEvents_unknownClassLoader() throws Exception { File privateCopyFile = privateFile("copied2.jar"); String expectedNameHash = "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93"; @@ -143,79 +152,66 @@ public final class DexLoggerIntegrationTests { } @Test - public void testDexLoggerGeneratesEvents_nativeLibrary() throws Exception { - File privateCopyFile = privateFile("copied.so"); - String expectedNameHash = - "996223BAD4B4FE75C57A3DEC61DB9C0B38E0A7AD479FC95F33494F4BC55A0F0E"; - String expectedContentHash = - copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); - - System.load(privateCopyFile.toString()); - - // Run the job to scan generated audit log entries - runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID); - - // And then make sure we log events about it - long previousEventNanos = mostRecentEventTimeNanos(); - runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID); - - assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, - expectedNameHash, expectedContentHash); + public void testGeneratesEvents_nativeLibrary() throws Exception { + new TestNativeCodeWithRetries() { + @Override + protected void loadNativeCode(int tryNumber) throws Exception { + // We need to use a different file name for each retry, because once a file is + // loaded, re-loading it has no effect. + String privateCopyName = "copied" + tryNumber + ".so"; + File privateCopyFile = privateFile(privateCopyName); + mExpectedNameHash = hashOf(privateCopyName); + mExpectedContentHash = copyAndHashResource( + libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); + + System.load(privateCopyFile.toString()); + } + }.runTest(); } @Test - public void testDexLoggerGeneratesEvents_nativeLibrary_escapedName() throws Exception { - // A file name with a space will be escaped in the audit log; verify we un-escape it - // correctly. - File privateCopyFile = privateFile("second copy.so"); - String expectedNameHash = - "8C39990C560B4F36F83E208E279F678746FE23A790E4C50F92686584EA2041CA"; - String expectedContentHash = - copyAndHashResource(libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); - - System.load(privateCopyFile.toString()); - - // Run the job to scan generated audit log entries - runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID); - - // And then make sure we log events about it - long previousEventNanos = mostRecentEventTimeNanos(); - runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID); - - assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, - expectedNameHash, expectedContentHash); + public void testGeneratesEvents_nativeLibrary_escapedName() throws Exception { + new TestNativeCodeWithRetries() { + @Override + protected void loadNativeCode(int tryNumber) throws Exception { + // A file name with a space will be escaped in the audit log; verify we un-escape it + // correctly. + String privateCopyName = "second copy " + tryNumber + ".so"; + File privateCopyFile = privateFile(privateCopyName); + mExpectedNameHash = hashOf(privateCopyName); + mExpectedContentHash = copyAndHashResource( + libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); + + System.load(privateCopyFile.toString()); + } + }.runTest(); } @Test - public void testDexLoggerGeneratesEvents_nativeExecutable() throws Exception { - File privateCopyFile = privateFile("test_executable"); - String expectedNameHash = - "3FBEC3F925A132D18F347F11AE9A5BB8DE1238828F8B4E064AA86EB68BD46DCF"; - String expectedContentHash = - copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile); - assertThat(privateCopyFile.setExecutable(true)).isTrue(); - - Process process = Runtime.getRuntime().exec(privateCopyFile.toString()); - int exitCode = process.waitFor(); - assertThat(exitCode).isEqualTo(0); - - // Run the job to scan generated audit log entries - runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID); - - // And then make sure we log events about it - long previousEventNanos = mostRecentEventTimeNanos(); - runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID); - - assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, - expectedNameHash, expectedContentHash); + public void testGeneratesEvents_nativeExecutable() throws Exception { + new TestNativeCodeWithRetries() { + @Override + protected void loadNativeCode(int tryNumber) throws Exception { + String privateCopyName = "test_executable" + tryNumber; + File privateCopyFile = privateFile(privateCopyName); + mExpectedNameHash = hashOf(privateCopyName); + mExpectedContentHash = copyAndHashResource( + "/DexLoggerNativeExecutable", privateCopyFile); + assertThat(privateCopyFile.setExecutable(true)).isTrue(); + + Process process = Runtime.getRuntime().exec(privateCopyFile.toString()); + int exitCode = process.waitFor(); + assertThat(exitCode).isEqualTo(0); + } + }.runTest(); } @Test - public void testDexLoggerGeneratesEvents_spoofed_validFile() throws Exception { + public void testGeneratesEvents_spoofed_validFile() throws Exception { File privateCopyFile = privateFile("spoofed"); - String expectedContentHash = - copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile); + String expectedContentHash = copyAndHashResource( + "/DexLoggerNativeExecutable", privateCopyFile); EventLog.writeEvent(EventLog.getTagCode("auditd"), "type=1400 avc: granted { execute_no_trans } " @@ -239,7 +235,7 @@ public final class DexLoggerIntegrationTests { } @Test - public void testDexLoggerGeneratesEvents_spoofed_pathTraversal() throws Exception { + public void testGeneratesEvents_spoofed_pathTraversal() throws Exception { File privateDir = privateFile("x").getParentFile(); // Transform /a/b/c -> /a/b/c/../../.. so we get back to the root @@ -276,7 +272,7 @@ public final class DexLoggerIntegrationTests { } @Test - public void testDexLoggerGeneratesEvents_spoofed_otherAppFile() throws Exception { + public void testGeneratesEvents_spoofed_otherAppFile() throws Exception { File ourPath = sContext.getDatabasePath("android_pay"); File targetPath = new File(ourPath.toString() .replace("com.android.frameworks.dexloggertest", "com.google.android.gms")); @@ -304,6 +300,40 @@ public final class DexLoggerIntegrationTests { assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash); } + // Abstract out the logic for running a native code loading test multiple times if needed and + // leaving time for audit messages to reach the log. + private abstract class TestNativeCodeWithRetries { + String mExpectedContentHash; + String mExpectedNameHash; + + abstract void loadNativeCode(int tryNumber) throws Exception; + + final void runTest() throws Exception { + List<String> messages = null; + + for (int i = 0; i < AUDIT_LOG_RETRIES; i++) { + loadNativeCode(i); + + SystemClock.sleep(i * RETRY_DELAY_MS); + + // Run the job to scan generated audit log entries + runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID); + + // And then make sure we log events about it + long previousEventNanos = mostRecentEventTimeNanos(); + runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID); + + messages = findMatchingEvents( + previousEventNanos, DCL_NATIVE_SUBTAG, mExpectedNameHash); + if (!messages.isEmpty()) { + break; + } + } + + assertHasDclLog(messages, mExpectedContentHash); + } + } + private static File privateFile(String name) { return new File(sContext.getDir("dcl", Context.MODE_PRIVATE), name); } @@ -315,7 +345,7 @@ public final class DexLoggerIntegrationTests { } private static String copyAndHashResource(String resourcePath, File copyTo) throws Exception { - MessageDigest hasher = MessageDigest.getInstance("SHA-256"); + MessageDigest hasher = MessageDigest.getInstance(SHA_256); // Copy the jar from our Java resources to a private data directory Class<?> thisClass = DexLoggerIntegrationTests.class; @@ -334,6 +364,16 @@ public final class DexLoggerIntegrationTests { // Compute the SHA-256 of the file content so we can check that it is the same as the value // we see logged. + return toHexString(hasher); + } + + private String hashOf(String input) throws Exception { + MessageDigest hasher = MessageDigest.getInstance(SHA_256); + hasher.update(input.getBytes()); + return toHexString(hasher); + } + + private static String toHexString(MessageDigest hasher) { Formatter formatter = new Formatter(); for (byte b : hasher.digest()) { formatter.format("%02X", b); @@ -388,6 +428,10 @@ public final class DexLoggerIntegrationTests { List<String> messages = findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash); + assertHasDclLog(messages, expectedContentHash); + } + + private static void assertHasDclLog(List<String> messages, String expectedContentHash) { assertWithMessage("Expected exactly one matching log entry").that(messages).hasSize(1); assertThat(messages.get(0)).endsWith(expectedContentHash); } diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp new file mode 100644 index 000000000000..37d3f5d4d97f --- /dev/null +++ b/tests/HwAccelerationTest/Android.bp @@ -0,0 +1,22 @@ +// +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "HwAccelerationTest", + srcs: ["**/*.java"], + platform_apis: true, + certificate: "platform", +} 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/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java index cb6a83d2644b..39608a40b416 100644 --- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java +++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java @@ -56,7 +56,7 @@ public class ColorExtractorTest { @Test public void ColorExtractor_extractWhenInitialized() { ExtractionType type = mock(Tonal.class); - new ColorExtractor(mContext, type); + new ColorExtractor(mContext, type, true); // 1 for lock and 1 for system verify(type, times(2)) .extractInto(any(), any(), any(), any()); @@ -83,7 +83,7 @@ public class ColorExtractorTest { outGradientColorsDark.set(colorsExpectedDark); outGradientColorsExtraDark.set(colorsExpectedExtraDark); }; - ColorExtractor extractor = new ColorExtractor(mContext, type); + ColorExtractor extractor = new ColorExtractor(mContext, type, true); GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM, ColorExtractor.TYPE_NORMAL); @@ -98,7 +98,7 @@ public class ColorExtractorTest { public void addOnColorsChangedListener_invokesListener() { ColorExtractor.OnColorsChangedListener mockedListeners = mock(ColorExtractor.OnColorsChangedListener.class); - ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext)); + ColorExtractor extractor = new ColorExtractor(mContext, new Tonal(mContext), true); extractor.addOnColorsChangedListener(mockedListeners); extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.RED), null, null), diff --git a/tests/JankBench/Android.bp b/tests/JankBench/Android.bp new file mode 100644 index 000000000000..166639d2e7db --- /dev/null +++ b/tests/JankBench/Android.bp @@ -0,0 +1,21 @@ +android_test { + name: "JankBench", + manifest: "app/src/main/AndroidManifest.xml", + sdk_version: "current", + // omit gradle 'build' dir + srcs: ["app/src/main/java/**/*.java"], + // use appcompat/support lib from the tree, so improvements/ + // regressions are reflected in test data + resource_dirs: ["app/src/main/res"], + static_libs: [ + "com.google.android.material_material", + "androidx.legacy_legacy-support-v4", + "androidx.appcompat_appcompat", + "androidx.cardview_cardview", + "androidx.recyclerview_recyclerview", + "androidx.leanback_leanback", + "apache-commons-math", + "junit", + ], + test_suites: ["device-tests"], +} diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk deleted file mode 100644 index 89c21b7fb605..000000000000 --- a/tests/JankBench/Android.mk +++ /dev/null @@ -1,38 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml - -LOCAL_SDK_VERSION := current - -LOCAL_USE_AAPT2 := true - -# omit gradle 'build' dir -LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java) - -# use appcompat/support lib from the tree, so improvements/ -# regressions are reflected in test data -LOCAL_RESOURCE_DIR := \ - $(LOCAL_PATH)/app/src/main/res \ - - -LOCAL_STATIC_ANDROID_LIBRARIES := \ - com.google.android.material_material \ - androidx.legacy_legacy-support-v4 \ - androidx.appcompat_appcompat \ - androidx.cardview_cardview \ - androidx.recyclerview_recyclerview \ - androidx.leanback_leanback \ - -LOCAL_STATIC_JAVA_LIBRARIES := \ - apache-commons-math \ - junit - - -LOCAL_PACKAGE_NAME := JankBench - -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/tests/JankBench/app/src/main/jni/Android.bp.converted b/tests/JankBench/app/src/main/jni/Android.bp.converted new file mode 100644 index 000000000000..9fecf1599fd9 --- /dev/null +++ b/tests/JankBench/app/src/main/jni/Android.bp.converted @@ -0,0 +1,27 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libnativebench", + cflags: [ + "-Wno-unused-parameter", + "-Wno-unused-variable", + ], + srcs: [ + "Bench.cpp", + "WorkerPool.cpp", + "test.cpp", + ], + host_ldlibs: ["-llog"], +} diff --git a/tests/JankBench/app/src/main/jni/Android.mk b/tests/JankBench/app/src/main/jni/Android.mk deleted file mode 100644 index 8ba874de0e8a..000000000000 --- a/tests/JankBench/app/src/main/jni/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2015 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -LOCAL_SDK_VERSION := 26 - -include $(CLEAR_VARS) - -LOCAL_CFLAGS = -Wno-unused-parameter - -LOCAL_MODULE:= libnativebench - -LOCAL_SRC_FILES := \ - Bench.cpp \ - WorkerPool.cpp \ - test.cpp - -LOCAL_LDLIBS := -llog - -include $(BUILD_SHARED_LIBRARY) diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk index 40d4eff53577..0967ad3767c6 100644 --- a/tests/RollbackTest/Android.mk +++ b/tests/RollbackTest/Android.mk @@ -71,7 +71,7 @@ ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE) # RollbackTest include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src) LOCAL_PACKAGE_NAME := RollbackTest LOCAL_MODULE_TAGS := tests LOCAL_STATIC_JAVA_LIBRARIES := android-support-test @@ -84,10 +84,21 @@ LOCAL_JAVA_RESOURCE_FILES := \ $(ROLLBACK_TEST_APP_BV1) \ $(ROLLBACK_TEST_APP_BV2) \ $(ROLLBACK_TEST_APEX_V2) +LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml LOCAL_SDK_VERSION := system_current LOCAL_TEST_CONFIG := RollbackTest.xml include $(BUILD_PACKAGE) +# StagedRollbackTest +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-java-files-under, StagedRollbackTest/src) +LOCAL_MODULE := StagedRollbackTest +LOCAL_MODULE_TAGS := tests +LOCAL_JAVA_LIBRARIES := tradefed +LOCAL_COMPATIBILITY_SUITE := general-tests +LOCAL_TEST_CONFIG := StagedRollbackTest.xml +include $(BUILD_HOST_JAVA_LIBRARY) + # Clean up local variables ROLLBACK_TEST_APP_AV1 := ROLLBACK_TEST_APP_AV2 := diff --git a/tests/RollbackTest/README.txt b/tests/RollbackTest/README.txt new file mode 100644 index 000000000000..c0b718a3e2c1 --- /dev/null +++ b/tests/RollbackTest/README.txt @@ -0,0 +1,23 @@ +This directory contains a test for the rollback manager service. + +Directory structure +=================== +RollbackTest + - device driven test for rollbacks not involving staged rollbacks. + +StagedRollbackTest + - device driven test for staged rollbacks. + +TestApp + - source for dummy apks used in testing. + +TestApex + - source for dummy apex modules used in testing. + +Running the tests +================= + +You can manually run the tests as follows: + + atest RollbackTest + atest StagedRollbackTest diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml index adbad56cd934..ac39f853656a 100644 --- a/tests/RollbackTest/RollbackTest.xml +++ b/tests/RollbackTest/RollbackTest.xml @@ -21,5 +21,9 @@ <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.tests.rollback" /> <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + + <!-- Exclude the StagedRollbackTest tests, which needs to be specially + driven from the StagedRollbackTest host test --> + <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" /> </test> </configuration> diff --git a/tests/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml index e57a768ad1b5..e57a768ad1b5 100644 --- a/tests/RollbackTest/AndroidManifest.xml +++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml diff --git a/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java index ddcf1dabcafc..ddcf1dabcafc 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/LocalIntentSender.java diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java index e10f866c899f..e10f866c899f 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackBroadcastReceiver.java diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index f07ae9f65b1b..f3edf09c5e95 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -16,6 +16,10 @@ package com.android.tests.rollback; +import static com.android.tests.rollback.RollbackTestUtils.assertPackageRollbackInfoEquals; +import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals; +import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage; + import android.Manifest; import android.app.ActivityManager; import android.content.BroadcastReceiver; @@ -24,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.VersionedPackage; -import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; @@ -42,7 +45,6 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Collections; -import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; @@ -617,17 +619,6 @@ public class RollbackTest { } } - // Helper function to test the value of a PackageRollbackInfo - private void assertPackageRollbackInfoEquals(String packageName, - long versionRolledBackFrom, long versionRolledBackTo, - PackageRollbackInfo info) { - assertEquals(packageName, info.getPackageName()); - assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName()); - assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode()); - assertEquals(packageName, info.getVersionRolledBackTo().getPackageName()); - assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode()); - } - /** * Test bad update automatic rollback. */ @@ -713,23 +704,6 @@ public class RollbackTest { } } - // Helper function to test the value of a RollbackInfo with single package - private void assertRollbackInfoEquals(String packageName, - long versionRolledBackFrom, long versionRolledBackTo, - RollbackInfo info, VersionedPackage... causePackages) { - assertNotNull(info); - assertEquals(1, info.getPackages().size()); - assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo, - info.getPackages().get(0)); - assertEquals(causePackages.length, info.getCausePackages().size()); - for (int i = 0; i < causePackages.length; ++i) { - assertEquals(causePackages[i].getPackageName(), - info.getCausePackages().get(i).getPackageName()); - assertEquals(causePackages[i].getLongVersionCode(), - info.getCausePackages().get(i).getLongVersionCode()); - } - } - // Helper function to test that the given rollback info is a rollback for // the atomic set {A2, B2} -> {A1, B1}. private void assertRollbackInfoForAandB(RollbackInfo rollback) { @@ -743,23 +717,4 @@ public class RollbackTest { assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1)); } } - - // Helper function to return the RollbackInfo with a given package in the - // list of rollbacks. Throws an assertion failure if there is more than - // one such rollback info. Returns null if there are no such rollback - // infos. - private RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks, - String packageName) { - RollbackInfo found = null; - for (RollbackInfo rollback : rollbacks) { - for (PackageRollbackInfo info : rollback.getPackages()) { - if (packageName.equals(info.getPackageName())) { - assertNull(found); - found = rollback; - break; - } - } - } - return found; - } } diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java index 60c7a59d9456..280ee1d1c576 100644 --- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java @@ -16,19 +16,31 @@ package com.android.tests.rollback; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.support.test.InstrumentationRegistry; +import android.util.Log; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; /** * Utilities to facilitate testing rollbacks. @@ -157,20 +169,28 @@ class RollbackTestUtils { /** * Installs the apks with the given resource names as an atomic set. + * <p> + * In case of staged installs, this function will return succesfully after + * the staged install has been committed and is ready for the device to + * reboot. * + * @param staged if the rollback should be staged. * @param enableRollback if rollback should be enabled. * @param resourceNames names of the class loader resource for the apks to * install. * @throws AssertionError if the installation fails. */ - static void installMultiPackage(boolean enableRollback, String... resourceNames) - throws InterruptedException, IOException { + private static void install(boolean staged, boolean enableRollback, + String... resourceNames) throws InterruptedException, IOException { Context context = InstrumentationRegistry.getContext(); PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); multiPackageParams.setMultiPackage(); + if (staged) { + multiPackageParams.setStaged(); + } if (enableRollback) { // TODO: Do we set this on the parent params, the child params, or // both? @@ -183,6 +203,9 @@ class RollbackTestUtils { PackageInstaller.Session session = null; PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); + if (staged) { + params.setStaged(); + } if (enableRollback) { params.setEnableRollback(); } @@ -204,6 +227,36 @@ class RollbackTestUtils { // Commit the session (this will start the installation workflow). multiPackage.commit(LocalIntentSender.getIntentSender()); assertStatusSuccess(LocalIntentSender.getIntentSenderResult()); + + if (staged) { + waitForSessionReady(multiPackageId); + } + } + + /** + * Installs the apks with the given resource names as an atomic set. + * + * @param enableRollback if rollback should be enabled. + * @param resourceNames names of the class loader resource for the apks to + * install. + * @throws AssertionError if the installation fails. + */ + static void installMultiPackage(boolean enableRollback, String... resourceNames) + throws InterruptedException, IOException { + install(false, enableRollback, resourceNames); + } + + /** + * Installs the apks with the given resource names as a staged atomic set. + * + * @param enableRollback if rollback should be enabled. + * @param resourceNames names of the class loader resource for the apks to + * install. + * @throws AssertionError if the installation fails. + */ + static void installStaged(boolean enableRollback, String... resourceNames) + throws InterruptedException, IOException { + install(true, enableRollback, resourceNames); } static void adoptShellPermissionIdentity(String... permissions) { @@ -219,4 +272,104 @@ class RollbackTestUtils { .getUiAutomation() .dropShellPermissionIdentity(); } + + /** + * Returns the RollbackInfo with a given package in the list of rollbacks. + * Throws an assertion failure if there is more than one such rollback + * info. Returns null if there are no such rollback infos. + */ + static RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks, + String packageName) { + RollbackInfo found = null; + for (RollbackInfo rollback : rollbacks) { + for (PackageRollbackInfo info : rollback.getPackages()) { + if (packageName.equals(info.getPackageName())) { + assertNull(found); + found = rollback; + break; + } + } + } + return found; + } + + /** + * Asserts that the given PackageRollbackInfo has the expected package + * name and versions. + */ + static void assertPackageRollbackInfoEquals(String packageName, + long versionRolledBackFrom, long versionRolledBackTo, + PackageRollbackInfo info) { + assertEquals(packageName, info.getPackageName()); + assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName()); + assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode()); + assertEquals(packageName, info.getVersionRolledBackTo().getPackageName()); + assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode()); + } + + /** + * Asserts that the given RollbackInfo has a single package with expected + * package name and versions. + */ + static void assertRollbackInfoEquals(String packageName, + long versionRolledBackFrom, long versionRolledBackTo, + RollbackInfo info, VersionedPackage... causePackages) { + assertNotNull(info); + assertEquals(1, info.getPackages().size()); + assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo, + info.getPackages().get(0)); + assertEquals(causePackages.length, info.getCausePackages().size()); + for (int i = 0; i < causePackages.length; ++i) { + assertEquals(causePackages[i].getPackageName(), + info.getCausePackages().get(i).getPackageName()); + assertEquals(causePackages[i].getLongVersionCode(), + info.getCausePackages().get(i).getLongVersionCode()); + } + } + + /** + * Waits for the given session to be marked as ready. + * Throws an assertion if the session fails. + */ + static void waitForSessionReady(int sessionId) { + BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>(); + BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + PackageInstaller.SessionInfo info = + intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); + if (info != null && info.getSessionId() == sessionId) { + if (info.isSessionReady() || info.isSessionFailed()) { + try { + sessionStatus.put(info); + } catch (InterruptedException e) { + Log.e(TAG, "Failed to put session info.", e); + } + } + } + } + }; + IntentFilter sessionUpdatedFilter = + new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED); + + Context context = InstrumentationRegistry.getContext(); + context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter); + + PackageInstaller installer = context.getPackageManager().getPackageInstaller(); + PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId); + + try { + if (info.isSessionReady() || info.isSessionFailed()) { + sessionStatus.put(info); + } + + info = sessionStatus.take(); + context.unregisterReceiver(sessionUpdatedReceiver); + if (info.isSessionFailed()) { + throw new AssertionError(info.getStagedSessionErrorMessage()); + } + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java new file mode 100644 index 000000000000..297bf869278f --- /dev/null +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -0,0 +1,131 @@ +/* + * 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.tests.rollback; + +import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals; +import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage; + +import android.Manifest; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for rollback of staged installs. + * <p> + * Note: These tests require reboot in between test phases. They are run + * specially so that the testFooEnableRollback, testFooCommitRollback, and + * testFooConfirmRollback phases of each test are run in order with reboots in + * between them. + */ +@RunWith(JUnit4.class) +public class StagedRollbackTest { + + private static final String TAG = "RollbackTest"; + private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A"; + + /** + * Adopts common shell permissions needed for rollback tests. + */ + @Before + public void adoptShellPermissions() { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.MANAGE_ROLLBACKS); + } + + /** + * Drops shell permissions needed for rollback tests. + */ + @After + public void dropShellPermissions() { + RollbackTestUtils.dropShellPermissionIdentity(); + } + + + /** + * Test basic rollbacks. Enable rollback phase. + */ + @Test + public void testBasicEnableRollback() throws Exception { + RollbackTestUtils.uninstall(TEST_APP_A); + assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackTestUtils.installStaged(true, "RollbackTestAppAv2.apk"); + + // At this point, the host test driver will reboot the device and run + // testBasicCommitRollback(). + } + + /** + * Test basic rollbacks. Commit rollback phase. + */ + @Test + public void testBasicCommitRollback() throws Exception { + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + assertTrue(rollback.isStaged()); + + RollbackTestUtils.rollback(rollback.getRollbackId()); + + rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + assertTrue(rollback.isStaged()); + assertNotEquals(-1, rollback.getCommittedSessionId()); + + RollbackTestUtils.waitForSessionReady(rollback.getCommittedSessionId()); + + // The app should not be rolled back until after reboot. + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + // At this point, the host test driver will reboot the device and run + // testBasicConfirmRollback(). + } + + /** + * Test basic rollbacks. Confirm rollback phase. + */ + @Test + public void testBasicConfirmRollback() throws Exception { + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getRecentlyCommittedRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + assertTrue(rollback.isStaged()); + assertNotEquals(-1, rollback.getCommittedSessionId()); + } +} diff --git a/tests/RollbackTest/StagedRollbackTest.xml b/tests/RollbackTest/StagedRollbackTest.xml new file mode 100644 index 000000000000..2750d3765c20 --- /dev/null +++ b/tests/RollbackTest/StagedRollbackTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs the staged rollback tests"> + <option name="test-suite-tag" value="StagedRollbackTest" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="RollbackTest.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" /> + </test> +</configuration> diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java new file mode 100644 index 000000000000..6cb0dd091392 --- /dev/null +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -0,0 +1,56 @@ +/* + * 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.tests.rollback.host; + +import static org.junit.Assert.assertTrue; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Runs the staged rollback tests. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class StagedRollbackTest extends BaseHostJUnit4Test { + + /** + * Runs the given phase of a test by calling into the device. + * Throws an exception if the test phase fails. + * <p> + * For example, <code>runPhase("testBasicEnableRollback");</code> + */ + private void runPhase(String phase) throws Exception { + assertTrue(runDeviceTests("com.android.tests.rollback", + "com.android.tests.rollback.StagedRollbackTest", + phase)); + } + + /** + * Tests staged rollbacks. + */ + @Test + public void testBasic() throws Exception { + runPhase("testBasicEnableRollback"); + getDevice().reboot(); + runPhase("testBasicCommitRollback"); + getDevice().reboot(); + runPhase("testBasicConfirmRollback"); + } +} diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING index c1d95ac85c23..6be93a0a199b 100644 --- a/tests/RollbackTest/TEST_MAPPING +++ b/tests/RollbackTest/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "RollbackTest" + }, + { + "name": "StagedRollbackTest" } ] } diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp new file mode 100644 index 000000000000..e0608e288459 --- /dev/null +++ b/tests/UiBench/Android.bp @@ -0,0 +1,19 @@ +android_test { + name: "UiBench", + sdk_version: "current", + min_sdk_version: "21", + // omit gradle 'build' dir + srcs: ["src/**/*.java"], + // use appcompat/support lib from the tree, so improvements/ + // regressions are reflected in test data + resource_dirs: ["res"], + static_libs: [ + "com.google.android.material_material", + "androidx.legacy_legacy-support-v4", + "androidx.appcompat_appcompat", + "androidx.cardview_cardview", + "androidx.recyclerview_recyclerview", + "androidx.leanback_leanback", + ], + test_suites: ["device-tests"], +} diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk deleted file mode 100644 index 608bf2f226f5..000000000000 --- a/tests/UiBench/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 - -# omit gradle 'build' dir -LOCAL_SRC_FILES := $(call all-java-files-under,src) - -# use appcompat/support lib from the tree, so improvements/ -# regressions are reflected in test data -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_USE_AAPT2 := true - -LOCAL_STATIC_ANDROID_LIBRARIES := \ - com.google.android.material_material \ - androidx.legacy_legacy-support-v4 \ - androidx.appcompat_appcompat \ - androidx.cardview_cardview \ - androidx.recyclerview_recyclerview \ - androidx.leanback_leanback - -LOCAL_PACKAGE_NAME := UiBench - -LOCAL_COMPATIBILITY_SUITE := device-tests - -include $(BUILD_PACKAGE) diff --git a/tests/UsageStatsTest/Android.bp b/tests/UsageStatsTest/Android.bp new file mode 100644 index 000000000000..0808b05ec053 --- /dev/null +++ b/tests/UsageStatsTest/Android.bp @@ -0,0 +1,8 @@ +android_test { + name: "UsageStatsTest", + // Only compile source java files in this apk. + srcs: ["src/**/*.java"], + static_libs: ["androidx.legacy_legacy-support-v4"], + certificate: "platform", + platform_apis: true, +} diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk deleted file mode 100644 index 5eed38c86657..000000000000 --- a/tests/UsageStatsTest/Android.mk +++ /dev/null @@ -1,17 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -# Only compile source java files in this apk. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_USE_AAPT2 := true -LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4 - -LOCAL_CERTIFICATE := platform - -LOCAL_PACKAGE_NAME := UsageStatsTest -LOCAL_PRIVATE_PLATFORM_APIS := true - -include $(BUILD_PACKAGE) diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp new file mode 100644 index 000000000000..c7c4b10a8405 --- /dev/null +++ b/tests/libs-permissions/Android.bp @@ -0,0 +1,29 @@ +java_library { + name: "com.android.test.libs.product", + installable: true, + product_specific: true, + srcs: ["product/java/**/*.java"], + required: ["com.android.test.libs.product.xml"], +} + +prebuilt_etc { + name: "com.android.test.libs.product.xml", + src: "product/com.android.test.libs.product.xml", + sub_dir: "permissions", + product_specific: true, +} + +java_library { + name: "com.android.test.libs.product_services", + installable: true, + product_services_specific: true, + srcs: ["product_services/java/**/*.java"], + required: ["com.android.test.libs.product_services.xml"], +} + +prebuilt_etc { + name: "com.android.test.libs.product_services.xml", + src: "product_services/com.android.test.libs.product_services.xml", + sub_dir: "permissions", + product_services_specific: true, +} diff --git a/tests/libs-permissions/Android.mk b/tests/libs-permissions/Android.mk deleted file mode 100644 index f4250c8b4e97..000000000000 --- a/tests/libs-permissions/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := com.android.test.libs.product -LOCAL_PRODUCT_MODULE := true -LOCAL_SRC_FILES := $(call all-java-files-under, product/java) -LOCAL_REQUIRED_MODULES := com.android.test.libs.product.xml -include $(BUILD_JAVA_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := com.android.test.libs.product.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions -LOCAL_SRC_FILES:= product/com.android.test.libs.product.xml -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_MODULE := com.android.test.libs.product_services -LOCAL_PRODUCT_SERVICES_MODULE := true -LOCAL_SRC_FILES := $(call all-java-files-under, product_services/java) -LOCAL_REQUIRED_MODULES := com.android.test.libs.product_services.xml -include $(BUILD_JAVA_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := com.android.test.libs.product_services.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/permissions -LOCAL_SRC_FILES:= product_services/com.android.test.libs.product_services.xml -include $(BUILD_PREBUILT) 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..b5d1ff9a0298 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -28,6 +28,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.RouteInfo.RTN_UNREACHABLE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -89,6 +90,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; @@ -566,7 +568,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 +579,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 +593,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 +604,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 +615,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, @@ -775,6 +777,16 @@ public class VpnTest { // V4 does not, but V6 has sufficient coverage again lp.addRoute(new RouteInfo(new IpPrefix("::/1"))); assertTrue(Vpn.providesRoutesToMostDestinations(lp)); + + lp.clear(); + // V4-unreachable route should not be treated as sufficient coverage + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + assertFalse(Vpn.providesRoutesToMostDestinations(lp)); + + lp.clear(); + // V6-unreachable route should not be treated as sufficient coverage + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + assertFalse(Vpn.providesRoutesToMostDestinations(lp)); } @Test diff --git a/tests/privapp-permissions/Android.bp b/tests/privapp-permissions/Android.bp new file mode 100644 index 000000000000..ca7864f047c7 --- /dev/null +++ b/tests/privapp-permissions/Android.bp @@ -0,0 +1,61 @@ +android_app { + name: "PrivAppPermissionTest", + sdk_version: "current", + privileged: true, + manifest: "system/AndroidManifest.xml", + required: ["privapp-permissions-test.xml"], +} + +prebuilt_etc { + name: "privapp-permissions-test.xml", + src: "system/privapp-permissions-test.xml", + sub_dir: "permissions", +} + +android_app { + name: "VendorPrivAppPermissionTest", + sdk_version: "current", + privileged: true, + manifest: "vendor/AndroidManifest.xml", + vendor: true, + required: ["vendorprivapp-permissions-test.xml"], +} + +prebuilt_etc { + name: "vendorprivapp-permissions-test.xml", + src: "vendor/privapp-permissions-test.xml", + sub_dir: "permissions", + proprietary: true, +} + +android_app { + name: "ProductPrivAppPermissionTest", + sdk_version: "current", + privileged: true, + manifest: "product/AndroidManifest.xml", + product_specific: true, + required: ["productprivapp-permissions-test.xml"], +} + +prebuilt_etc { + name: "productprivapp-permissions-test.xml", + src: "product/privapp-permissions-test.xml", + sub_dir: "permissions", + product_specific: true, +} + +android_app { + name: "ProductServicesPrivAppPermissionTest", + sdk_version: "current", + privileged: true, + manifest: "product_services/AndroidManifest.xml", + product_services_specific: true, + required: ["product_servicesprivapp-permissions-test.xml"], +} + +prebuilt_etc { + name: "product_servicesprivapp-permissions-test.xml", + src: "product_services/privapp-permissions-test.xml", + sub_dir: "permissions", + product_services_specific: true, +} diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk deleted file mode 100644 index 1149b8a8fe40..000000000000 --- a/tests/privapp-permissions/Android.mk +++ /dev/null @@ -1,64 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := PrivAppPermissionTest -LOCAL_SDK_VERSION := current -LOCAL_PRIVILEGED_MODULE := true -LOCAL_MANIFEST_FILE := system/AndroidManifest.xml -LOCAL_REQUIRED_MODULES := privapp-permissions-test.xml -include $(BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_MODULE := privapp-permissions-test.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions -LOCAL_SRC_FILES:= system/privapp-permissions-test.xml -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := VendorPrivAppPermissionTest -LOCAL_SDK_VERSION := current -LOCAL_PRIVILEGED_MODULE := true -LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml -LOCAL_VENDOR_MODULE := true -LOCAL_REQUIRED_MODULES := vendorprivapp-permissions-test.xml -include $(BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_MODULE := vendorprivapp-permissions-test.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/permissions -LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest -LOCAL_SDK_VERSION := current -LOCAL_PRIVILEGED_MODULE := true -LOCAL_MANIFEST_FILE := product/AndroidManifest.xml -LOCAL_PRODUCT_MODULE := true -LOCAL_REQUIRED_MODULES := productprivapp-permissions-test.xml -include $(BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_MODULE := productprivapp-permissions-test.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions -LOCAL_SRC_FILES:= product/privapp-permissions-test.xml -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := ProductServicesPrivAppPermissionTest -LOCAL_SDK_VERSION := current -LOCAL_PRIVILEGED_MODULE := true -LOCAL_MANIFEST_FILE := product_services/AndroidManifest.xml -LOCAL_PRODUCT_SERVICES_MODULE := true -LOCAL_REQUIRED_MODULES := product_servicesprivapp-permissions-test.xml -include $(BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_MODULE := product_servicesprivapp-permissions-test.xml -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/permissions -LOCAL_SRC_FILES:= product_services/privapp-permissions-test.xml -include $(BUILD_PREBUILT) 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..d0237f80a8f0 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 << "> " @@ -1623,7 +1637,9 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser, if (!(plural->values[index] = ParseXml( parser, android::ResTable_map::TYPE_STRING, kNoRawString))) { error = true; + continue; } + plural->values[index]->SetSource(item_source); } else if (!ShouldIgnoreElement(element_namespace, 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 73b568e77689..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>. @@ -151,10 +151,12 @@ message Overlayable { // Represents an overlayable <item> declaration within an <overlayable> tag. message OverlayableItem { enum Policy { - PUBLIC = 0; - SYSTEM = 1; - VENDOR = 2; - PRODUCT = 3; + NONE = 0; + PUBLIC = 1; + SYSTEM = 2; + VENDOR = 3; + PRODUCT = 4; + SIGNATURE = 5; } // The location of the <item> declaration in source. 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..22edd2f2055a 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -268,6 +268,7 @@ struct ResourceFileFlattenerOptions { bool update_proguard_spec = false; OutputFormat output_format = OutputFormat::kApk; std::unordered_set<std::string> extensions_to_not_compress; + Maybe<std::regex> regex_to_not_compress; }; // A sampling of public framework resource IDs. @@ -377,11 +378,18 @@ ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& } } +// TODO(rtmitchell): turn this function into a variable that points to a method that retrieves the +// compression flag uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) { if (options_.do_not_compress_anything) { return 0; } + if (options_.regex_to_not_compress + && std::regex_search(str.to_string(), options_.regex_to_not_compress.value())) { + return 0; + } + for (const std::string& extension : options_.extensions_to_not_compress) { if (util::EndsWith(str, extension)) { return 0; @@ -400,7 +408,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> @@ -1530,7 +1539,11 @@ class Linker { for (auto& entry : merged_assets) { uint32_t compression_flags = ArchiveEntry::kCompress; std::string extension = file::GetExtension(entry.first).to_string(); - if (options_.extensions_to_not_compress.count(extension) > 0) { + + if (options_.do_not_compress_anything + || options_.extensions_to_not_compress.count(extension) > 0 + || (options_.regex_to_not_compress + && std::regex_search(extension, options_.regex_to_not_compress.value()))) { compression_flags = 0u; } @@ -1558,6 +1571,7 @@ class Linker { file_flattener_options.keep_raw_values = keep_raw_values; file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything; file_flattener_options.extensions_to_not_compress = options_.extensions_to_not_compress; + file_flattener_options.regex_to_not_compress = options_.regex_to_not_compress; file_flattener_options.no_auto_version = options_.no_auto_version; file_flattener_options.no_version_vectors = options_.no_version_vectors; file_flattener_options.no_version_transitions = options_.no_version_transitions; @@ -2165,6 +2179,20 @@ int LinkCommand::Action(const std::vector<std::string>& args) { } } + if (no_compress_regex) { + std::string regex = no_compress_regex.value(); + if (util::StartsWith(regex, "@")) { + const std::string path = regex.substr(1, regex.size() -1); + std::string error; + if (!file::AppendSetArgsFromFile(path, &options_.extensions_to_not_compress, &error)) { + context.GetDiagnostics()->Error(DiagMessage(path) << error); + return 1; + } + } else { + options_.regex_to_not_compress = GetRegularExpression(no_compress_regex.value()); + } + } + // Populate some default no-compress extensions that are already compressed. options_.extensions_to_not_compress.insert( {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 590a6bb19e30..1fc149ab41af 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -17,6 +17,8 @@ #ifndef AAPT2_LINK_H #define AAPT2_LINK_H +#include <regex> + #include "Command.h" #include "Diagnostics.h" #include "Resource.h" @@ -63,6 +65,7 @@ struct LinkOptions { bool no_xml_namespaces = false; bool do_not_compress_anything = false; std::unordered_set<std::string> extensions_to_not_compress; + Maybe<std::regex> regex_to_not_compress; // Static lib options. bool no_static_lib_packages = false; @@ -250,6 +253,11 @@ class LinkCommand : public Command { &options_.do_not_compress_anything); AddOptionalSwitch("--keep-raw-values", "Preserve raw attribute values in xml files.", &options_.keep_raw_values); + AddOptionalFlag("--no-compress-regex", + "Do not compress extensions matching the regular expression. Remember to\n" + " use the '$' symbol for end of line. Uses a non case-sensitive\n" + " ECMAScript regular expression grammar.", + &no_compress_regex); AddOptionalSwitch("--warn-manifest-validation", "Treat manifest validation errors as warnings.", &options_.manifest_fixer_options.warn_validation); @@ -283,6 +291,7 @@ class LinkCommand : public Command { std::vector<std::string> configs_; Maybe<std::string> preferred_density_; Maybe<std::string> product_list_; + Maybe<std::string> no_compress_regex; bool legacy_x_flag_ = false; bool require_localization_ = false; bool verbose_ = false; diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index 792120e449ae..e2c65ba74271 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -435,4 +435,11 @@ void SetLongVersionCode(xml::Element* manifest, uint64_t version) { } } +std::regex GetRegularExpression(const std::string &input) { + // Standard ECMAScript grammar plus case insensitive. + std::regex case_insensitive( + input, std::regex_constants::icase | std::regex_constants::ECMAScript); + return case_insensitive; +} + } // namespace aapt diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h index cf1443e30e1f..2a7c62eef678 100644 --- a/tools/aapt2/cmd/Util.h +++ b/tools/aapt2/cmd/Util.h @@ -17,6 +17,8 @@ #ifndef AAPT_SPLIT_UTIL_H #define AAPT_SPLIT_UTIL_H +#include <regex> + #include "androidfw/StringPiece.h" #include "AppInfo.h" @@ -72,6 +74,9 @@ std::string MakePackageSafeName(const std::string &name); // versionCodeMajor if the version code requires more than 32 bits. void SetLongVersionCode(xml::Element* manifest, uint64_t version_code); +// Returns a case insensitive regular expression based on the input. +std::regex GetRegularExpression(const std::string &input); + } // namespace aapt #endif /* AAPT_SPLIT_UTIL_H */ diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp index f92f1e3c4c7e..7e492610b658 100644 --- a/tools/aapt2/cmd/Util_test.cpp +++ b/tools/aapt2/cmd/Util_test.cpp @@ -383,4 +383,12 @@ TEST (UtilTest, AdjustSplitConstraintsForMinSdk) { EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig()); } +TEST(UtilTest, RegularExperssions) { + std::string valid(".bc$"); + std::regex expression = GetRegularExpression(valid); + EXPECT_TRUE(std::regex_search("file.abc", expression)); + EXPECT_TRUE(std::regex_search("file.123bc", expression)); + EXPECT_FALSE(std::regex_search("abc.zip", expression)); +} + } // namespace aapt 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/tools/aapt2/integration-tests/Android.mk b/tools/aapt2/integration-tests/Android.mk deleted file mode 100644 index 6361f9b8ae7d..000000000000 --- a/tools/aapt2/integration-tests/Android.mk +++ /dev/null @@ -1,2 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp new file mode 100644 index 000000000000..79fb5734cd68 --- /dev/null +++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp @@ -0,0 +1,20 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "AaptAutoVersionTest", + sdk_version: "current", +} diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.mk b/tools/aapt2/integration-tests/AutoVersionTest/Android.mk deleted file mode 100644 index 03cce3534a4e..000000000000 --- a/tools/aapt2/integration-tests/AutoVersionTest/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_PACKAGE_NAME := AaptAutoVersionTest -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp new file mode 100644 index 000000000000..a94a01f12c9e --- /dev/null +++ b/tools/aapt2/integration-tests/BasicTest/Android.bp @@ -0,0 +1,20 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "AaptBasicTest", + sdk_version: "current", +} diff --git a/tools/aapt2/integration-tests/BasicTest/Android.mk b/tools/aapt2/integration-tests/BasicTest/Android.mk deleted file mode 100644 index d1605540371e..000000000000 --- a/tools/aapt2/integration-tests/BasicTest/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_PACKAGE_NAME := AaptBasicTest -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/StaticLibTest/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/Android.mk deleted file mode 100644 index 6361f9b8ae7d..000000000000 --- a/tools/aapt2/integration-tests/StaticLibTest/Android.mk +++ /dev/null @@ -1,2 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp new file mode 100644 index 000000000000..9aadff3d619e --- /dev/null +++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + + name: "AaptTestStaticLib_App", + sdk_version: "current", + srcs: ["src/**/*.java"], + asset_dirs: [ + "assets", + "assets2", + ], + static_libs: [ + "AaptTestStaticLib_LibOne", + "AaptTestStaticLib_LibTwo", + ], + aaptflags: [ + "--no-version-vectors", + "--no-version-transitions", + ], +} diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk deleted file mode 100644 index 3cce35de6a31..000000000000 --- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -# -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_PACKAGE_NAME := AaptTestStaticLib_App -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2 -LOCAL_STATIC_ANDROID_LIBRARIES := \ - AaptTestStaticLib_LibOne \ - AaptTestStaticLib_LibTwo -LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp new file mode 100644 index 000000000000..4c8181343a33 --- /dev/null +++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp @@ -0,0 +1,22 @@ +// +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_library { + name: "AaptTestStaticLib_LibOne", + sdk_version: "current", + srcs: ["src/**/*.java"], + resource_dirs: ["res"], +} diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk deleted file mode 100644 index da25f6477b08..000000000000 --- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_MODULE := AaptTestStaticLib_LibOne -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac. -LOCAL_JAR_EXCLUDE_FILES := none -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp new file mode 100644 index 000000000000..7c4f7ed90e69 --- /dev/null +++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_library { + name: "AaptTestStaticLib_LibTwo", + sdk_version: "current", + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + libs: ["AaptTestStaticLib_LibOne"], +} diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk deleted file mode 100644 index 27a3134e6ffa..000000000000 --- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -# -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_MODULE := AaptTestStaticLib_LibTwo -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne -include $(BUILD_STATIC_JAVA_LIBRARY) - diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp new file mode 100644 index 000000000000..68e6148e480c --- /dev/null +++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp @@ -0,0 +1,20 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "AaptSymlinkTest", + sdk_version: "current", +} diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.mk b/tools/aapt2/integration-tests/SymlinkTest/Android.mk deleted file mode 100644 index 8da1141df7b3..000000000000 --- a/tools/aapt2/integration-tests/SymlinkTest/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_PACKAGE_NAME := AaptSymlinkTest -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 7b268bb283f4..604b2575daa1 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -251,6 +251,25 @@ bool AppendArgsFromFile(const StringPiece& path, std::vector<std::string>* out_a return true; } +bool AppendSetArgsFromFile(const StringPiece& path, std::unordered_set<std::string>* out_argset, + std::string* out_error) { + std::string contents; + if(!ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) { + if (out_error) { + *out_error = "failed to read argument-list file"; + } + return false; + } + + for (StringPiece line : util::Tokenize(contents, ' ')) { + line = util::TrimWhitespace(line); + if (!line.empty()) { + out_argset->insert(line.to_string()); + } + } + return true; +} + bool FileFilter::SetPattern(const StringPiece& pattern) { pattern_tokens_ = util::SplitAndLowercase(pattern, ':'); return true; diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index 58395526b193..481a4cdb6ad0 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -19,6 +19,7 @@ #include <memory> #include <string> +#include <unordered_set> #include <vector> #include "android-base/macros.h" @@ -86,6 +87,10 @@ Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist, std::string* out_error); +// Reads the file at path and appends each line to the outargset set. +bool AppendSetArgsFromFile(const android::StringPiece& path, + std::unordered_set<std::string>* out_argset, std::string* out_error); + // Filter that determines which resource files/directories are // processed by AAPT. Takes a pattern string supplied by the user. // Pattern format is specified in the FileFilter::SetPattern() method. 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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 5e5a59566ce5..7caace6ec854 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2136,17 +2136,26 @@ public class WifiManager { } /** + * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)} + * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and + * {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}. + * * @return true if this adapter supports Device-to-device RTT * @hide */ + @Deprecated @SystemApi public boolean isDeviceToDeviceRttSupported() { return isFeatureSupported(WIFI_FEATURE_D2D_RTT); } /** + * @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)} + * with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT}. + * * @return true if this adapter supports Device-to-AP RTT */ + @Deprecated public boolean isDeviceToApRttSupported() { return isFeatureSupported(WIFI_FEATURE_D2AP_RTT); } 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; } |