diff options
780 files changed, 26298 insertions, 7514 deletions
diff --git a/Android.bp b/Android.bp index 74f19e1d68da..d1332bb4fe87 100644 --- a/Android.bp +++ b/Android.bp @@ -117,7 +117,7 @@ java_library { "core/java/android/content/ISyncServiceAdapter.aidl", "core/java/android/content/ISyncStatusObserver.aidl", "core/java/android/content/om/IOverlayManager.aidl", - "core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl", + "core/java/android/content/pm/ICrossProfileApps.aidl", "core/java/android/content/pm/IDexModuleRegisterCallback.aidl", "core/java/android/content/pm/ILauncherApps.aidl", "core/java/android/content/pm/IOnAppsChangedListener.aidl", @@ -243,6 +243,7 @@ java_library { "core/java/android/security/IKeystoreService.aidl", "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl", "core/java/android/service/autofill/IAutoFillService.aidl", + "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl", "core/java/android/service/autofill/IFillCallback.aidl", "core/java/android/service/autofill/ISaveCallback.aidl", "core/java/android/service/carrier/ICarrierService.aidl", @@ -467,6 +468,8 @@ java_library { "telecomm/java/com/android/internal/telecom/IInCallService.aidl", "telecomm/java/com/android/internal/telecom/ITelecomService.aidl", "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl", + "telephony/java/android/telephony/data/IDataService.aidl", + "telephony/java/android/telephony/data/IDataServiceCallback.aidl", "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl", "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl", "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl", @@ -499,6 +502,7 @@ java_library { "telephony/java/com/android/ims/internal/IImsService.aidl", "telephony/java/com/android/ims/internal/IImsServiceController.aidl", "telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl", + "telephony/java/com/android/ims/internal/IImsSmsListener.aidl", "telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl", "telephony/java/com/android/ims/internal/IImsUt.aidl", "telephony/java/com/android/ims/internal/IImsUtListener.aidl", @@ -522,9 +526,31 @@ java_library { "telephony/java/com/android/internal/telephony/ITelephony.aidl", "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl", "telephony/java/com/android/internal/telephony/IWapPushManager.aidl", + "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl", "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl", "telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl", "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl", + "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl", + "wifi/java/android/net/wifi/ISoftApCallback.aidl", "wifi/java/android/net/wifi/IWifiManager.aidl", "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl", "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl", @@ -664,7 +690,10 @@ gensrcs { " $(in) " + "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", - srcs: ["core/proto/**/*.proto"], + srcs: [ + "core/proto/**/*.proto", + "libs/incident/**/*.proto", + ], output_extension: "srcjar", } diff --git a/Android.mk b/Android.mk index 3c6dd37acefe..2254008422e0 100644 --- a/Android.mk +++ b/Android.mk @@ -72,12 +72,6 @@ non_base_dirs := \ ../opt/net/voip/src/java/android/net/rtp \ ../opt/net/voip/src/java/android/net/sip \ -framework_base_android_test_base_src_files := \ - $(call all-java-files-under, test-base/src/junit) - -framework_base_android_test_runner_src_files := \ - $(call all-java-files-under, test-runner/src/junit) - # Find all files in specific directories (relative to frameworks/base) # to document and check apis files_to_check_apis := \ @@ -126,8 +120,6 @@ framework_docs_LOCAL_SRC_FILES := \ # These are relative to frameworks/base framework_docs_LOCAL_API_CHECK_SRC_FILES := \ - $(framework_base_android_test_base_src_files) \ - $(framework_base_android_test_runner_src_files) \ $(files_to_check_apis) \ $(common_src_files) \ @@ -339,6 +331,8 @@ LOCAL_DROIDDOC_OPTIONS:=\ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \ -referenceonly \ -api $(INTERNAL_PLATFORM_API_FILE) \ + -privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \ + -privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \ -removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \ -nodocs @@ -348,7 +342,9 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -$(INTERNAL_PLATFORM_API_FILE): $(full_target) +$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \ + $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \ + $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) # ==== the system api stubs =================================== @@ -373,6 +369,8 @@ LOCAL_DROIDDOC_OPTIONS:=\ -referenceonly \ -showAnnotation android.annotation.SystemApi \ -api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \ + -privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \ + -privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \ -removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \ -exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \ -nodocs @@ -383,7 +381,9 @@ LOCAL_UNINSTALLABLE_MODULE := true include $(BUILD_DROIDDOC) -$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target) +$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \ + $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \ + $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) # ==== the test api stubs =================================== @@ -789,6 +789,7 @@ LOCAL_PROTOC_FLAGS := \ LOCAL_SOURCE_FILES_ALL_GENERATED := true LOCAL_SRC_FILES := \ cmds/am/proto/instrumentation_data.proto \ + cmds/statsd/src/perfetto/perfetto_config.proto \ $(call all-proto-files-under, core/proto) \ $(call all-proto-files-under, libs/incident/proto) \ $(call all-proto-files-under, cmds/statsd/src) @@ -805,7 +806,8 @@ LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ store_unknown_fields = true LOCAL_JAVA_LIBRARIES := core-oj core-libart LOCAL_SRC_FILES := \ - $(call all-proto-files-under, core/proto) + $(call all-proto-files-under, core/proto) \ + $(call all-proto-files-under, libs/incident/proto/android/os) include $(BUILD_STATIC_JAVA_LIBRARY) @@ -817,7 +819,8 @@ LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_PROTOC_FLAGS := \ -Iexternal/protobuf/src LOCAL_SRC_FILES := \ - $(call all-proto-files-under, core/proto) + $(call all-proto-files-under, core/proto) \ + $(call all-proto-files-under, libs/incident/proto/android/os) include $(BUILD_STATIC_JAVA_LIBRARY) # Include subdirectory makefiles @@ -829,4 +832,5 @@ ifeq (,$(ONE_SHOT_MAKEFILE)) include $(call first-makefiles-under,$(LOCAL_PATH)) endif -endif # ANDROID_BUILD_EMBEDDED
\ No newline at end of file +endif # ANDROID_BUILD_EMBEDDED + diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml index bd0b944e1233..132a2f9b2d0c 100644 --- a/apct-tests/perftests/core/AndroidManifest.xml +++ b/apct-tests/perftests/core/AndroidManifest.xml @@ -10,7 +10,11 @@ <application> <uses-library android:name="android.test.runner" /> - <activity android:name="android.perftests.utils.StubActivity" /> + <activity android:name="android.perftests.utils.StubActivity"> + <intent-filter> + <action android:name="com.android.perftests.core.PERFTEST" /> + </intent-filter> + </activity> <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" /> </application> diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java new file mode 100644 index 000000000000..145fbcd2d5d7 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.os; + +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class PackageManagerPerfTest { + private static final String PERMISSION_NAME_EXISTS = + "com.android.perftests.core.TestPermission"; + private static final String PERMISSION_NAME_DOESNT_EXIST = + "com.android.perftests.core.TestBadPermission"; + private static final ComponentName TEST_ACTIVITY = + new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity"); + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void testCheckPermissionExists() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName); + } + } + + @Test + public void testCheckPermissionDoesntExist() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName); + } + } + + @Test + public void testQueryIntentActivities() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final Intent intent = new Intent("com.android.perftests.core.PERFTEST"); + + while (state.keepRunning()) { + pm.queryIntentActivities(intent, 0); + } + } + + @Test + public void testGetPackageInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + pm.getPackageInfo(packageName, 0); + } + } + + @Test + public void testGetApplicationInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + final String packageName = TEST_ACTIVITY.getPackageName(); + + while (state.keepRunning()) { + pm.getApplicationInfo(packageName, 0); + } + } + + @Test + public void testGetActivityInfo() throws Exception { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager(); + + while (state.keepRunning()) { + pm.getActivityInfo(TEST_ACTIVITY, 0); + } + } +} diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java deleted file mode 100644 index d292e7dc3b96..000000000000 --- a/apct-tests/perftests/core/src/android/os/PermissionTest.java +++ /dev/null @@ -1,66 +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 android.os; - -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import android.content.Context; -import android.perftests.utils.BenchmarkState; -import android.perftests.utils.PerfStatusReporter; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@LargeTest -public class PermissionTest { - private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission"; - private static final String PERMISSION_DOESNT_HAVE_NAME = - "com.android.perftests.core.TestBadPermission"; - private static final String THIS_PACKAGE_NAME = "com.android.perftests.core"; - - @Rule - public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - - @Test - public void testHasPermission() { - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final Context context = InstrumentationRegistry.getTargetContext(); - while (state.keepRunning()) { - int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME, - THIS_PACKAGE_NAME); - } - } - - @Test - public void testDoesntHavePermission() { - final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final Context context = InstrumentationRegistry.getTargetContext(); - - while (state.keepRunning()) { - int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME, - THIS_PACKAGE_NAME); - } - } - -} diff --git a/apct-tests/perftests/core/src/android/os/PssPerfTest.java b/apct-tests/perftests/core/src/android/os/PssPerfTest.java new file mode 100644 index 000000000000..400115deeeb8 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/PssPerfTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.os; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class PssPerfTest { + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void testPss() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Debug.getPss(); + } + } +} diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 93a0fc314b7f..682885b3120d 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -25,10 +25,14 @@ import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import android.content.res.ColorStateList; +import android.graphics.Canvas; import android.graphics.Typeface; import android.text.Layout; import android.text.style.TextAppearanceSpan; +import android.view.DisplayListCanvas; +import android.view.RenderNode; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,7 +56,7 @@ public class StaticLayoutPerfTest { private static final boolean NO_STYLE_TEXT = false; private static final boolean STYLE_TEXT = true; - private final Random mRandom = new Random(31415926535L); + private Random mRandom; private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; private static final int ALPHABET_LENGTH = ALPHABET.length(); @@ -98,6 +102,11 @@ public class StaticLayoutPerfTest { return ssb; } + @Before + public void setUp() { + mRandom = new Random(0); + } + @Test public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); @@ -190,8 +199,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = MeasuredText.build( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -206,8 +218,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = MeasuredText.build( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -222,8 +237,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = MeasuredText.build( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -238,8 +256,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = MeasuredText.build( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -254,8 +275,11 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final MeasuredText text = MeasuredText.build( - generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -264,4 +288,157 @@ public class StaticLayoutPerfTest { .build(); } } + + @Test + public void testDraw_FixedText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_Styled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_Styled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_RandomText_NoStyled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_Styled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_NoStyled() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_Styled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + + @Test + public void testDraw_MeasuredText_NoStyled_WithoutCache() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final RenderNode node = RenderNode.create("benchmark", null); + while (state.keepRunning()) { + state.pauseTiming(); + final MeasuredText text = new MeasuredText.Builder( + generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + final StaticLayout layout = + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); + final DisplayListCanvas c = node.start(1200, 200); + Canvas.freeTextLayoutCaches(); + state.resumeTiming(); + + layout.draw(c); + } + } + } diff --git a/api/current.txt b/api/current.txt index 2f3d8f0660b6..52dbe9b23128 100644 --- a/api/current.txt +++ b/api/current.txt @@ -211,6 +211,7 @@ package android { field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 field public static final int accessibilityFlags = 16843652; // 0x1010384 + field public static final int accessibilityHeading = 16844160; // 0x1010580 field public static final int accessibilityLiveRegion = 16843758; // 0x10103ee field public static final int accessibilityPaneTitle = 16844156; // 0x101057c field public static final int accessibilityTraversalAfter = 16843986; // 0x10104d2 @@ -602,6 +603,7 @@ package android { field public static final int fingerprintAuthDrawable = 16844008; // 0x10104e8 field public static final int finishOnCloseSystemDialogs = 16843431; // 0x10102a7 field public static final int finishOnTaskLaunch = 16842772; // 0x1010014 + field public static final int firstBaselineToTopHeight = 16844157; // 0x101057d field public static final int firstDayOfWeek = 16843581; // 0x101033d field public static final int fitsSystemWindows = 16842973; // 0x10100dd field public static final int flipInterval = 16843129; // 0x1010179 @@ -798,6 +800,7 @@ package android { field public static final int largeHeap = 16843610; // 0x101035a field public static final int largeScreens = 16843398; // 0x1010286 field public static final int largestWidthLimitDp = 16843622; // 0x1010366 + field public static final int lastBaselineToBottomHeight = 16844158; // 0x101057e field public static final int launchMode = 16842781; // 0x101001d field public static final int launchTaskBehindSourceAnimation = 16843922; // 0x1010492 field public static final int launchTaskBehindTargetAnimation = 16843921; // 0x1010491 @@ -855,6 +858,7 @@ package android { field public static final int left = 16843181; // 0x10101ad field public static final int letterSpacing = 16843958; // 0x10104b6 field public static final int level = 16844032; // 0x1010500 + field public static final int lineHeight = 16844159; // 0x101057f field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 field public static final int lines = 16843092; // 0x1010154 @@ -2792,6 +2796,7 @@ package android.accessibilityservice { field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6 field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5 field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3 + field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9 field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7 field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService"; field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice"; @@ -5856,11 +5861,15 @@ package android.app { method public java.lang.CharSequence getLabel(); method public java.lang.String getResultKey(); method public static android.os.Bundle getResultsFromIntent(android.content.Intent); + method public static int getResultsSource(android.content.Intent); method public boolean isDataOnly(); + method public static void setResultsSource(android.content.Intent, int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR; field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData"; field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results"; + field public static final int SOURCE_CHOICE = 1; // 0x1 + field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0 } public static final class RemoteInput.Builder { @@ -6338,6 +6347,9 @@ package android.app.admin { method public void onTransferOwnershipComplete(android.content.Context, android.os.PersistableBundle); method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle); method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle); + method public void onUserStarted(android.content.Context, android.content.Intent, android.os.UserHandle); + method public void onUserStopped(android.content.Context, android.content.Intent, android.os.UserHandle); + method public void onUserSwitched(android.content.Context, android.content.Intent, android.os.UserHandle); field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED"; @@ -6354,6 +6366,7 @@ package android.app.admin { field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING"; field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; field public static final java.lang.String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE"; + field public static final java.lang.String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = "android.app.support_transfer_ownership"; } public class DeviceAdminService extends android.app.Service { @@ -6397,6 +6410,7 @@ package android.app.admin { method public java.util.List<java.lang.String> getDelegatePackages(android.content.ComponentName, java.lang.String); method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String); method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); + method public java.lang.CharSequence getEndUserSessionMessage(android.content.ComponentName); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); @@ -6432,6 +6446,7 @@ package android.app.admin { method public boolean getScreenCaptureDisabled(android.content.ComponentName); method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName); method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName); + method public java.lang.CharSequence getStartUserSessionMessage(android.content.ComponentName); method public boolean getStorageEncryption(android.content.ComponentName); method public int getStorageEncryptionStatus(); method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); @@ -6459,6 +6474,7 @@ package android.app.admin { method public boolean isMasterVolumeMuted(android.content.ComponentName); method public boolean isNetworkLoggingEnabled(android.content.ComponentName); method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; + method public boolean isPrintingEnabled(); method public boolean isProfileOwnerApp(java.lang.String); method public boolean isProvisioningAllowed(java.lang.String); method public boolean isResetPasswordTokenActive(android.content.ComponentName); @@ -6494,6 +6510,7 @@ package android.app.admin { method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>); method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); + method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean); @@ -6527,6 +6544,7 @@ package android.app.admin { method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>); + method public void setPrintingEnabled(android.content.ComponentName, boolean); method public void setProfileEnabled(android.content.ComponentName); method public void setProfileName(android.content.ComponentName, java.lang.String); method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo); @@ -6537,6 +6555,7 @@ package android.app.admin { method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public void setSecurityLoggingEnabled(android.content.ComponentName, boolean); method public void setShortSupportMessage(android.content.ComponentName, java.lang.CharSequence); + method public void setStartUserSessionMessage(android.content.ComponentName, java.lang.CharSequence); method public boolean setStatusBarDisabled(android.content.ComponentName, boolean); method public int setStorageEncryption(android.content.ComponentName, boolean); method public void setSystemSetting(android.content.ComponentName, java.lang.String, java.lang.String); @@ -6656,6 +6675,7 @@ package android.app.admin { field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0 field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera"; field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture"; + field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups"; field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2 field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1 field public static final int SKIP_SETUP_WIZARD = 1; // 0x1 @@ -6979,6 +6999,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long); method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean); + method public android.app.job.JobInfo.Builder setIsPrefetch(boolean); method public android.app.job.JobInfo.Builder setMinimumLatency(long); method public android.app.job.JobInfo.Builder setOverrideDeadline(long); method public android.app.job.JobInfo.Builder setPeriodic(long); @@ -7086,6 +7107,7 @@ package android.app.slice { field public static final java.lang.String HINT_MAX = "max"; field public static final java.lang.String HINT_NO_TINT = "no_tint"; field public static final java.lang.String HINT_PARTIAL = "partial"; + field public static final java.lang.String HINT_SEE_MORE = "see_more"; field public static final java.lang.String HINT_SELECTED = "selected"; field public static final java.lang.String HINT_SHORTCUT = "shortcut"; field public static final java.lang.String HINT_SUMMARY = "summary"; @@ -7234,6 +7256,7 @@ package android.app.usage { public static class NetworkStats.Bucket { ctor public NetworkStats.Bucket(); + method public int getDefaultNetwork(); method public long getEndTimeStamp(); method public int getMetered(); method public int getRoaming(); @@ -7245,6 +7268,9 @@ package android.app.usage { method public long getTxBytes(); method public long getTxPackets(); method public int getUid(); + field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff + field public static final int DEFAULT_NETWORK_NO = 1; // 0x1 + field public static final int DEFAULT_NETWORK_YES = 2; // 0x2 field public static final int METERED_ALL = -1; // 0xffffffff field public static final int METERED_NO = 1; // 0x1 field public static final int METERED_YES = 2; // 0x2 @@ -10674,6 +10700,13 @@ package android.content.pm { field public int reqTouchScreen; } + public class CrossProfileApps { + method public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(android.os.UserHandle); + method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle); + method public java.util.List<android.os.UserHandle> getTargetUserProfiles(); + method public void startMainActivity(android.content.ComponentName, android.os.UserHandle); + } + public final class FeatureGroupInfo implements android.os.Parcelable { ctor public FeatureGroupInfo(); ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo); @@ -11468,17 +11501,6 @@ package android.content.pm { } -package android.content.pm.crossprofile { - - public class CrossProfileApps { - method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle); - method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle); - method public java.util.List<android.os.UserHandle> getTargetUserProfiles(); - method public void startMainActivity(android.content.ComponentName, android.os.UserHandle); - } - -} - package android.content.res { public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable { @@ -15219,18 +15241,26 @@ package android.hardware { method public void writeToParcel(android.os.Parcel, int); field public static final int BLOB = 33; // 0x21 field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR; + field public static final int DS_24UI8 = 50; // 0x32 + field public static final int DS_FP32UI8 = 52; // 0x34 + field public static final int D_16 = 48; // 0x30 + field public static final int D_24 = 49; // 0x31 + field public static final int D_FP32 = 51; // 0x33 field public static final int RGBA_1010102 = 43; // 0x2b field public static final int RGBA_8888 = 1; // 0x1 field public static final int RGBA_FP16 = 22; // 0x16 field public static final int RGBX_8888 = 2; // 0x2 field public static final int RGB_565 = 4; // 0x4 field public static final int RGB_888 = 3; // 0x3 + field public static final int S_UI8 = 53; // 0x35 field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L + field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L + field public static final long USAGE_GPU_MIPMAP_COMPLETE = 67108864L; // 0x4000000L field public static final long USAGE_GPU_SAMPLED_IMAGE = 256L; // 0x100L field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L @@ -15716,6 +15746,7 @@ package android.hardware.camera2 { field public static final int CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3; // 0x3 field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH = 2; // 0x2 field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; // 0x4 + field public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5; // 0x5 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1 @@ -15814,6 +15845,7 @@ package android.hardware.camera2 { field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3 + field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; // 0x4 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2 field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0 @@ -21722,7 +21754,13 @@ package android.media { field public static final int CHANNEL_OUT_STEREO = 12; // 0xc field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR; + field public static final int ENCODING_AAC_ELD = 15; // 0xf + field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb + field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc + field public static final int ENCODING_AAC_LC = 10; // 0xa + field public static final int ENCODING_AAC_XHE = 16; // 0x10 field public static final int ENCODING_AC3 = 5; // 0x5 + field public static final int ENCODING_AC4 = 17; // 0x11 field public static final int ENCODING_DEFAULT = 1; // 0x1 field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe field public static final int ENCODING_DTS = 7; // 0x7 @@ -21730,6 +21768,7 @@ package android.media { field public static final int ENCODING_E_AC3 = 6; // 0x6 field public static final int ENCODING_IEC61937 = 13; // 0xd field public static final int ENCODING_INVALID = 0; // 0x0 + field public static final int ENCODING_MP3 = 9; // 0x9 field public static final int ENCODING_PCM_16BIT = 2; // 0x2 field public static final int ENCODING_PCM_8BIT = 3; // 0x3 field public static final int ENCODING_PCM_FLOAT = 4; // 0x4 @@ -21772,6 +21811,7 @@ package android.media { method public boolean isBluetoothScoOn(); method public boolean isMicrophoneMute(); method public boolean isMusicActive(); + method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat); method public boolean isSpeakerphoneOn(); method public boolean isStreamMute(int); method public boolean isVolumeFixed(); @@ -22072,6 +22112,7 @@ package android.media { method public int reloadStaticData(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener); + method public void removeStreamEventCallback(); method public int setAuxEffectSendLevel(float); method public int setBufferSizeInFrames(int); method public int setLoopPoints(int, int, int); @@ -22085,6 +22126,7 @@ package android.media { method public boolean setPreferredDevice(android.media.AudioDeviceInfo); method protected deprecated void setState(int); method public deprecated int setStereoVolume(float, float); + method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback); method public int setVolume(float); method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); @@ -22120,6 +22162,7 @@ package android.media { method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; + method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean); method public android.media.AudioTrack.Builder setPerformanceMode(int); method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException; method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException; @@ -22135,6 +22178,12 @@ package android.media { method public default void onRoutingChanged(android.media.AudioRouting); } + public static abstract class AudioTrack.StreamEventCallback { + method public void onStreamDataRequest(android.media.AudioTrack); + method public void onStreamPresentationEnd(android.media.AudioTrack); + method public void onTearDown(android.media.AudioTrack); + } + public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -26229,9 +26278,9 @@ package android.net { method public void applyTransportModeTransform(java.io.FileDescriptor, int, android.net.IpSecTransform) throws java.io.IOException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; - method public void removeTransportModeTransforms(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException; - method public void removeTransportModeTransforms(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException; - method public void removeTransportModeTransforms(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException; + method public void removeTransportModeTransforms(java.net.Socket) throws java.io.IOException; + method public void removeTransportModeTransforms(java.net.DatagramSocket) throws java.io.IOException; + method public void removeTransportModeTransforms(java.io.FileDescriptor) throws java.io.IOException; field public static final int DIRECTION_IN = 0; // 0x0 field public static final int DIRECTION_OUT = 1; // 0x1 } @@ -26284,7 +26333,9 @@ package android.net { method public android.net.ProxyInfo getHttpProxy(); method public java.lang.String getInterfaceName(); method public java.util.List<android.net.LinkAddress> getLinkAddresses(); + method public java.lang.String getPrivateDnsServerName(); method public java.util.List<android.net.RouteInfo> getRoutes(); + method public boolean isPrivateDnsActive(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR; } @@ -32212,6 +32263,7 @@ package android.os { field public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1; // 0xffffffff field public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; // 0xffffffed field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8 + field public static final int THREAD_PRIORITY_VIDEO = -10; // 0xfffffff6 } public abstract class ProxyFileDescriptorCallback { @@ -37892,7 +37944,6 @@ package android.service.autofill { } public static final class FieldClassification.Match { - method public java.lang.String getAlgorithm(); method public java.lang.String getRemoteId(); method public float getScore(); } @@ -42304,10 +42355,10 @@ package android.text { } public class MeasuredText implements android.text.Spanned { - method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic); - method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int); method public char charAt(int); + method public int getBreakStrategy(); method public int getEnd(); + method public int getHyphenationFrequency(); method public android.text.TextPaint getPaint(); method public int getParagraphCount(); method public int getParagraphEnd(int); @@ -42324,6 +42375,15 @@ package android.text { method public java.lang.CharSequence subSequence(int, int); } + public static final class MeasuredText.Builder { + ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint); + method public android.text.MeasuredText build(); + method public android.text.MeasuredText.Builder setBreakStrategy(int); + method public android.text.MeasuredText.Builder setHyphenationFrequency(int); + method public android.text.MeasuredText.Builder setRange(int, int); + method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic); + } + public abstract interface NoCopySpan { } @@ -44060,6 +44120,18 @@ package android.util { field public static final deprecated boolean RELEASE = true; } + public class DataUnit extends java.lang.Enum { + method public long toBytes(long); + method public static android.util.DataUnit valueOf(java.lang.String); + method public static final android.util.DataUnit[] values(); + enum_constant public static final android.util.DataUnit GIBIBYTES; + enum_constant public static final android.util.DataUnit GIGABYTES; + enum_constant public static final android.util.DataUnit KIBIBYTES; + enum_constant public static final android.util.DataUnit KILOBYTES; + enum_constant public static final android.util.DataUnit MEBIBYTES; + enum_constant public static final android.util.DataUnit MEGABYTES; + } + public class DebugUtils { method public static boolean isObjectSelected(java.lang.Object); } @@ -48180,6 +48252,7 @@ package android.view.accessibility { method public boolean isEnabled(); method public boolean isFocusable(); method public boolean isFocused(); + method public boolean isHeading(); method public boolean isImportantForAccessibility(); method public boolean isLongClickable(); method public boolean isMultiLine(); @@ -48223,6 +48296,7 @@ package android.view.accessibility { method public void setError(java.lang.CharSequence); method public void setFocusable(boolean); method public void setFocused(boolean); + method public void setHeading(boolean); method public void setHintText(java.lang.CharSequence); method public void setImportantForAccessibility(boolean); method public void setInputType(int); @@ -48356,7 +48430,7 @@ package android.view.accessibility { method public int getColumnSpan(); method public int getRowIndex(); method public int getRowSpan(); - method public boolean isHeading(); + method public deprecated boolean isHeading(); method public boolean isSelected(); method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean); method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean); @@ -52515,6 +52589,7 @@ package android.widget { method public int getExtendedPaddingBottom(); method public int getExtendedPaddingTop(); method public android.text.InputFilter[] getFilters(); + method public int getFirstBaselineToTopHeight(); method public java.lang.String getFontFeatureSettings(); method public java.lang.String getFontVariationSettings(); method public boolean getFreezesText(); @@ -52532,6 +52607,7 @@ package android.widget { method public int getInputType(); method public int getJustificationMode(); method public final android.text.method.KeyListener getKeyListener(); + method public int getLastBaselineToBottomHeight(); method public final android.text.Layout getLayout(); method public float getLetterSpacing(); method public int getLineBounds(int, android.graphics.Rect); @@ -52579,6 +52655,7 @@ package android.widget { method public android.graphics.Typeface getTypeface(); method public android.text.style.URLSpan[] getUrls(); method public boolean hasSelection(); + method public boolean isAccessibilityHeading(); method public boolean isAllCaps(); method public boolean isCursorVisible(); method public boolean isElegantTextHeight(); @@ -52601,6 +52678,7 @@ package android.widget { method protected void onTextChanged(java.lang.CharSequence, int, int, int); method public boolean onTextContextMenuItem(int); method public void removeTextChangedListener(android.text.TextWatcher); + method public void setAccessibilityHeading(boolean); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int); @@ -52628,6 +52706,7 @@ package android.widget { method public void setExtractedText(android.view.inputmethod.ExtractedText); method public void setFallbackLineSpacing(boolean); method public void setFilters(android.text.InputFilter[]); + method public void setFirstBaselineToTopHeight(int); method public void setFontFeatureSettings(java.lang.String); method public boolean setFontVariationSettings(java.lang.String); method protected boolean setFrame(int, int, int, int); @@ -52649,7 +52728,9 @@ package android.widget { method public void setInputType(int); method public void setJustificationMode(int); method public void setKeyListener(android.text.method.KeyListener); + method public void setLastBaselineToBottomHeight(int); method public void setLetterSpacing(float); + method public void setLineHeight(int); method public void setLineSpacing(float, float); method public void setLines(int); method public final void setLinkTextColor(int); @@ -72399,197 +72480,6 @@ package javax.xml.xpath { } -package junit.framework { - - public class Assert { - ctor protected Assert(); - method public static void assertEquals(java.lang.String, java.lang.Object, java.lang.Object); - method public static void assertEquals(java.lang.Object, java.lang.Object); - method public static void assertEquals(java.lang.String, java.lang.String, java.lang.String); - method public static void assertEquals(java.lang.String, java.lang.String); - method public static void assertEquals(java.lang.String, double, double, double); - method public static void assertEquals(double, double, double); - method public static void assertEquals(java.lang.String, float, float, float); - method public static void assertEquals(float, float, float); - method public static void assertEquals(java.lang.String, long, long); - method public static void assertEquals(long, long); - method public static void assertEquals(java.lang.String, boolean, boolean); - method public static void assertEquals(boolean, boolean); - method public static void assertEquals(java.lang.String, byte, byte); - method public static void assertEquals(byte, byte); - method public static void assertEquals(java.lang.String, char, char); - method public static void assertEquals(char, char); - method public static void assertEquals(java.lang.String, short, short); - method public static void assertEquals(short, short); - method public static void assertEquals(java.lang.String, int, int); - method public static void assertEquals(int, int); - method public static void assertFalse(java.lang.String, boolean); - method public static void assertFalse(boolean); - method public static void assertNotNull(java.lang.Object); - method public static void assertNotNull(java.lang.String, java.lang.Object); - method public static void assertNotSame(java.lang.String, java.lang.Object, java.lang.Object); - method public static void assertNotSame(java.lang.Object, java.lang.Object); - method public static void assertNull(java.lang.Object); - method public static void assertNull(java.lang.String, java.lang.Object); - method public static void assertSame(java.lang.String, java.lang.Object, java.lang.Object); - method public static void assertSame(java.lang.Object, java.lang.Object); - method public static void assertTrue(java.lang.String, boolean); - method public static void assertTrue(boolean); - method public static void fail(java.lang.String); - method public static void fail(); - method public static void failNotEquals(java.lang.String, java.lang.Object, java.lang.Object); - method public static void failNotSame(java.lang.String, java.lang.Object, java.lang.Object); - method public static void failSame(java.lang.String); - method public static java.lang.String format(java.lang.String, java.lang.Object, java.lang.Object); - } - - public class AssertionFailedError extends java.lang.AssertionError { - ctor public AssertionFailedError(); - ctor public AssertionFailedError(java.lang.String); - } - - public class ComparisonFailure extends junit.framework.AssertionFailedError { - ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String); - method public java.lang.String getActual(); - method public java.lang.String getExpected(); - } - - public abstract interface Protectable { - method public abstract void protect() throws java.lang.Throwable; - } - - public abstract interface Test { - method public abstract int countTestCases(); - method public abstract void run(junit.framework.TestResult); - } - - public abstract class TestCase extends junit.framework.Assert implements junit.framework.Test { - ctor public TestCase(); - ctor public TestCase(java.lang.String); - method public int countTestCases(); - method protected junit.framework.TestResult createResult(); - method public java.lang.String getName(); - method public junit.framework.TestResult run(); - method public void run(junit.framework.TestResult); - method public void runBare() throws java.lang.Throwable; - method protected void runTest() throws java.lang.Throwable; - method public void setName(java.lang.String); - method protected void setUp() throws java.lang.Exception; - method protected void tearDown() throws java.lang.Exception; - } - - public class TestFailure { - ctor public TestFailure(junit.framework.Test, java.lang.Throwable); - method public java.lang.String exceptionMessage(); - method public junit.framework.Test failedTest(); - method public boolean isFailure(); - method public java.lang.Throwable thrownException(); - method public java.lang.String trace(); - field protected junit.framework.Test fFailedTest; - field protected java.lang.Throwable fThrownException; - } - - public abstract interface TestListener { - method public abstract void addError(junit.framework.Test, java.lang.Throwable); - method public abstract void addFailure(junit.framework.Test, junit.framework.AssertionFailedError); - method public abstract void endTest(junit.framework.Test); - method public abstract void startTest(junit.framework.Test); - } - - public class TestResult { - ctor public TestResult(); - method public synchronized void addError(junit.framework.Test, java.lang.Throwable); - method public synchronized void addFailure(junit.framework.Test, junit.framework.AssertionFailedError); - method public synchronized void addListener(junit.framework.TestListener); - method public void endTest(junit.framework.Test); - method public synchronized int errorCount(); - method public synchronized java.util.Enumeration<junit.framework.TestFailure> errors(); - method public synchronized int failureCount(); - method public synchronized java.util.Enumeration<junit.framework.TestFailure> failures(); - method public synchronized void removeListener(junit.framework.TestListener); - method protected void run(junit.framework.TestCase); - method public synchronized int runCount(); - method public void runProtected(junit.framework.Test, junit.framework.Protectable); - method public synchronized boolean shouldStop(); - method public void startTest(junit.framework.Test); - method public synchronized void stop(); - method public synchronized boolean wasSuccessful(); - field protected java.util.Vector<junit.framework.TestFailure> fErrors; - field protected java.util.Vector<junit.framework.TestFailure> fFailures; - field protected java.util.Vector<junit.framework.TestListener> fListeners; - field protected int fRunTests; - } - - public class TestSuite implements junit.framework.Test { - ctor public TestSuite(); - ctor public TestSuite(java.lang.Class<?>); - ctor public TestSuite(java.lang.Class<? extends junit.framework.TestCase>, java.lang.String); - ctor public TestSuite(java.lang.String); - ctor public TestSuite(java.lang.Class<?>...); - ctor public TestSuite(java.lang.Class<? extends junit.framework.TestCase>[], java.lang.String); - method public void addTest(junit.framework.Test); - method public void addTestSuite(java.lang.Class<? extends junit.framework.TestCase>); - method public int countTestCases(); - method public static junit.framework.Test createTest(java.lang.Class<?>, java.lang.String); - method public java.lang.String getName(); - method public static java.lang.reflect.Constructor<?> getTestConstructor(java.lang.Class<?>) throws java.lang.NoSuchMethodException; - method public void run(junit.framework.TestResult); - method public void runTest(junit.framework.Test, junit.framework.TestResult); - method public void setName(java.lang.String); - method public junit.framework.Test testAt(int); - method public int testCount(); - method public java.util.Enumeration<junit.framework.Test> tests(); - method public static junit.framework.Test warning(java.lang.String); - } - -} - -package junit.runner { - - public abstract class BaseTestRunner implements junit.framework.TestListener { - ctor public BaseTestRunner(); - method public synchronized void addError(junit.framework.Test, java.lang.Throwable); - method public synchronized void addFailure(junit.framework.Test, junit.framework.AssertionFailedError); - method protected void clearStatus(); - method public java.lang.String elapsedTimeAsString(long); - method public synchronized void endTest(junit.framework.Test); - method public java.lang.String extractClassName(java.lang.String); - method public static java.lang.String getFilteredTrace(java.lang.Throwable); - method public static java.lang.String getFilteredTrace(java.lang.String); - method public deprecated junit.runner.TestSuiteLoader getLoader(); - method public static java.lang.String getPreference(java.lang.String); - method public static int getPreference(java.lang.String, int); - method protected static java.util.Properties getPreferences(); - method public junit.framework.Test getTest(java.lang.String); - method public static deprecated boolean inVAJava(); - method protected java.lang.Class<?> loadSuiteClass(java.lang.String) throws java.lang.ClassNotFoundException; - method protected java.lang.String processArguments(java.lang.String[]); - method protected abstract void runFailed(java.lang.String); - method public static void savePreferences() throws java.io.IOException; - method public void setLoading(boolean); - method public void setPreference(java.lang.String, java.lang.String); - method protected static void setPreferences(java.util.Properties); - method protected static boolean showStackRaw(); - method public synchronized void startTest(junit.framework.Test); - method public abstract void testEnded(java.lang.String); - method public abstract void testFailed(int, junit.framework.Test, java.lang.Throwable); - method public abstract void testStarted(java.lang.String); - method public static java.lang.String truncate(java.lang.String); - method protected boolean useReloadingTestSuiteLoader(); - field public static final java.lang.String SUITE_METHODNAME = "suite"; - } - - public abstract interface TestSuiteLoader { - method public abstract java.lang.Class load(java.lang.String) throws java.lang.ClassNotFoundException; - method public abstract java.lang.Class reload(java.lang.Class) throws java.lang.ClassNotFoundException; - } - - public class Version { - method public static java.lang.String id(); - } - -} - package org.apache.http.conn { public deprecated class ConnectTimeoutException extends java.io.InterruptedIOException { diff --git a/api/system-current.txt b/api/system-current.txt index a520ed613faa..a1ec2c44cd49 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -381,6 +381,7 @@ package android.app.admin { method public java.lang.CharSequence getDeviceOwnerOrganizationName(); method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int); method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); + method public java.lang.CharSequence getPrintingDisabledReason(); method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; method public int getUserProvisioningState(); @@ -792,7 +793,9 @@ package android.content { field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; - field public static final java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; + field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.intent.action.SIM_APPLICATION_STATE_CHANGED"; + field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.intent.action.SIM_CARD_STATE_CHANGED"; + field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; @@ -1120,9 +1123,9 @@ package android.hardware.display { } public static class BrightnessConfiguration.Builder { - ctor public BrightnessConfiguration.Builder(); + ctor public BrightnessConfiguration.Builder(float[], float[]); method public android.hardware.display.BrightnessConfiguration build(); - method public android.hardware.display.BrightnessConfiguration.Builder setCurve(float[], float[]); + method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String); } public final class DisplayManager { @@ -1777,9 +1780,9 @@ package android.hardware.radio { method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int); method public long getFirstId(int); method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId(); - method public int getProgramType(); + method public deprecated int getProgramType(); method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds(); - method public long[] getVendorIds(); + method public deprecated long[] getVendorIds(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR; field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1 @@ -1787,27 +1790,31 @@ package android.hardware.radio { field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8 field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7 field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5 + field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5 field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa - field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb + field public static final deprecated int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9 field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3 - field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4 + field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714 + field public static final deprecated int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4 field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0 field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2 field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc - field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf - field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8 - field public static final int PROGRAM_TYPE_AM = 1; // 0x1 - field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3 - field public static final int PROGRAM_TYPE_DAB = 5; // 0x5 - field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6 - field public static final int PROGRAM_TYPE_FM = 2; // 0x2 - field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4 - field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0 - field public static final int PROGRAM_TYPE_SXM = 7; // 0x7 - field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf - field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8 + field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf + field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf + field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8 + field public static final int IDENTIFIER_TYPE_VENDOR_START = 1000; // 0x3e8 + field public static final deprecated int PROGRAM_TYPE_AM = 1; // 0x1 + field public static final deprecated int PROGRAM_TYPE_AM_HD = 3; // 0x3 + field public static final deprecated int PROGRAM_TYPE_DAB = 5; // 0x5 + field public static final deprecated int PROGRAM_TYPE_DRMO = 6; // 0x6 + field public static final deprecated int PROGRAM_TYPE_FM = 2; // 0x2 + field public static final deprecated int PROGRAM_TYPE_FM_HD = 4; // 0x4 + field public static final deprecated int PROGRAM_TYPE_INVALID = 0; // 0x0 + field public static final deprecated int PROGRAM_TYPE_SXM = 7; // 0x7 + field public static final deprecated int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf + field public static final deprecated int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8 } public static final class ProgramSelector.Identifier implements android.os.Parcelable { @@ -1822,7 +1829,7 @@ package android.hardware.radio { public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation { } - public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation { + public static abstract deprecated class ProgramSelector.ProgramType implements java.lang.annotation.Annotation { } public class RadioManager { @@ -2860,9 +2867,18 @@ package android.net { method public void onTetheringStarted(); } + public final class IpSecManager { + method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; + } + + public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable { + method public void close(); + method public java.lang.String getInterfaceName(); + } + public static class IpSecTransform.Builder { + method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; method public android.net.IpSecTransform.Builder setNattKeepalive(int); - method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network); } public class NetworkKey implements android.os.Parcelable { @@ -3575,6 +3591,7 @@ package android.os { method public boolean isRestrictedProfile(); field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED"; field public static final deprecated java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock"; + field public static final java.lang.String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; field public static final int RESTRICTION_NOT_SET = 0; // 0x0 field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2 field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4 @@ -3873,6 +3890,18 @@ package android.security.keystore { } +package android.service.autofill { + + public abstract class AutofillFieldClassificationService extends android.app.Service { + method public android.os.IBinder onBind(android.content.Intent); + method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>); + field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService"; + field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms"; + field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm"; + } + +} + package android.service.notification { public final class Adjustment implements android.os.Parcelable { @@ -4374,6 +4403,8 @@ package android.telephony { public class SubscriptionManager { method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int); + method public void setSubscriptionOverrideCongested(int, boolean, long); + method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>); field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS"; @@ -4455,6 +4486,8 @@ package android.telephony { method public deprecated boolean getDataEnabled(); method public deprecated boolean getDataEnabled(int); method public boolean getEmergencyCallbackMode(); + method public int getSimApplicationState(); + method public int getSimCardState(); method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method public android.os.Bundle getVisualVoicemailSettings(); method public int getVoiceActivationState(); @@ -4488,6 +4521,7 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff + field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2 @@ -4495,6 +4529,8 @@ package android.telephony { field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3 field public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; // 0x4 field public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; // 0x0 + field public static final int SIM_STATE_LOADED = 10; // 0xa + field public static final int SIM_STATE_PRESENT = 11; // 0xb } public abstract class VisualVoicemailService extends android.app.Service { @@ -4554,6 +4590,38 @@ package android.telephony.data { field public static final int TYPE_COMMON = 0; // 0x0 } + public abstract class DataService extends android.app.Service { + method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int); + field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID"; + field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService"; + } + + public class DataService.DataServiceProvider { + ctor public DataService.DataServiceProvider(int); + method public void deactivateDataCall(int, boolean, boolean, android.telephony.data.DataServiceCallback); + method public void getDataCallList(android.telephony.data.DataServiceCallback); + method public final int getSlotId(); + method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); + method protected void onDestroy(); + method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback); + method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback); + method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, boolean, android.net.LinkProperties, android.telephony.data.DataServiceCallback); + } + + public class DataServiceCallback { + method public void onDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); + method public void onDeactivateDataCallComplete(int); + method public void onGetDataCallListComplete(int, java.util.List<android.telephony.data.DataCallResponse>); + method public void onSetDataProfileComplete(int); + method public void onSetInitialAttachApnComplete(int); + method public void onSetupDataCallComplete(int, android.telephony.data.DataCallResponse); + field public static final int RESULT_ERROR_BUSY = 3; // 0x3 + field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4 + field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2 + field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1 + field public static final int RESULT_SUCCESS = 0; // 0x0 + } + } package android.telephony.ims { diff --git a/api/test-current.txt b/api/test-current.txt index 6941731c29cd..acc819e602d1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -320,9 +320,9 @@ package android.hardware.display { } public static class BrightnessConfiguration.Builder { - ctor public BrightnessConfiguration.Builder(); + ctor public BrightnessConfiguration.Builder(float[], float[]); method public android.hardware.display.BrightnessConfiguration build(); - method public android.hardware.display.BrightnessConfiguration.Builder setCurve(float[], float[]); + method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String); } public final class DisplayManager { @@ -563,11 +563,6 @@ package android.service.autofill { method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception; } - public final class EditDistanceScorer { - method public static android.service.autofill.EditDistanceScorer getInstance(); - method public float getScore(android.view.autofill.AutofillValue, java.lang.String); - } - public final class FillResponse implements android.os.Parcelable { method public int getFlags(); } @@ -966,6 +961,9 @@ package android.view { public final class Choreographer { method public static long getFrameDelay(); + method public void postCallback(int, java.lang.Runnable, java.lang.Object); + method public void postCallbackDelayed(int, java.lang.Runnable, java.lang.Object, long); + method public void removeCallbacks(int, java.lang.Runnable, java.lang.Object); method public static void setFrameDelay(long); field public static final int CALLBACK_ANIMATION = 1; // 0x1 } diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h index c41612de4eb3..166796673e25 100644 --- a/cmds/incident_helper/src/TextParserBase.h +++ b/cmds/incident_helper/src/TextParserBase.h @@ -68,4 +68,4 @@ public: virtual status_t Parse(const int in, const int out) const; }; -#endif // TEXT_PARSER_BASE_H
\ No newline at end of file +#endif // TEXT_PARSER_BASE_H diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 8420bc8f7ac7..368a70bc3898 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -134,6 +134,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md index ad0fa08c7326..71c6deb18aac 100644 --- a/cmds/incidentd/README.md +++ b/cmds/incidentd/README.md @@ -12,8 +12,8 @@ Run the test on a device manually ``` root$ mmm -j frameworks/base/cmds/incidentd && \ -adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \ -adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null +adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/ && \ +adb shell /data/nativetest/incidentd_test 2>/dev/null ``` Run the test via AndroidTest.xml diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc index 66667dca2982..1bd146850ea9 100644 --- a/cmds/incidentd/incidentd.rc +++ b/cmds/incidentd/incidentd.rc @@ -19,4 +19,4 @@ service incidentd /system/bin/incidentd on post-fs-data # Create directory for incidentd - mkdir /data/misc/incidents 0770 root root + mkdir /data/misc/incidents 0770 incidentd incidentd diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 30dd339a629b..0fff4e6dc4a0 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -63,12 +63,14 @@ FdBuffer::read(int fd, int64_t timeout) int64_t remainingTime = (mStartTime + timeout) - uptimeMillis(); if (remainingTime <= 0) { + if (DEBUG) ALOGD("timed out due to long read"); mTimedOut = true; break; } int count = poll(&pfds, 1, remainingTime); if (count == 0) { + if (DEBUG) ALOGD("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { @@ -129,6 +131,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis(); if (remainingTime <= 0) { + if (DEBUG) ALOGD("timed out due to long read"); mTimedOut = true; break; } @@ -136,6 +139,7 @@ FdBuffer::readProcessedDataInStream(int fd, int toFd, int fromFd, int64_t timeou // wait for any pfds to be ready to perform IO int count = poll(pfds, 3, remainingTime); if (count == 0) { + if (DEBUG) ALOGD("timed out due to block calling poll"); mTimedOut = true; break; } else if (count < 0) { diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index c4b54bbbc022..1d5ab59f9ba8 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -43,24 +43,44 @@ String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); static Status -checkIncidentPermissions() +checkIncidentPermissions(const IncidentReportArgs& args) { + uid_t callingUid = IPCThreadState::self()->getCallingUid(); + if (callingUid == AID_ROOT || callingUid == AID_SHELL) { + // root doesn't have permission.DUMP if don't do this! + return Status::ok(); + } + + // checking calling permission. if (!checkCallingPermission(DUMP_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP", - IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); + IPCThreadState::self()->getCallingPid(), callingUid); return Status::fromExceptionCode(Status::EX_SECURITY, "Calling process does not have permission: android.permission.DUMP"); } if (!checkCallingPermission(USAGE_STATS_PERMISSION)) { ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS", - IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); + IPCThreadState::self()->getCallingPid(), callingUid); return Status::fromExceptionCode(Status::EX_SECURITY, "Calling process does not have permission: android.permission.USAGE_STATS"); } + + // checking calling request uid permission. + switch (args.dest()) { + case DEST_LOCAL: + if (callingUid != AID_SHELL || callingUid != AID_ROOT) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Calling process does not have permission to get local data."); + } + case DEST_EXPLICIT: + if (callingUid != AID_SHELL || callingUid != AID_ROOT || + callingUid != AID_STATSD || callingUid != AID_SYSTEM) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Calling process does not have permission to get explicit data."); + } + } return Status::ok(); } - - // ================================================================================ ReportRequestQueue::ReportRequestQueue() { @@ -71,7 +91,7 @@ ReportRequestQueue::~ReportRequestQueue() } void -ReportRequestQueue::addRequest(const sp<ReportRequest>& request) +ReportRequestQueue::addRequest(const sp<ReportRequest>& request) { unique_lock<mutex> lock(mLock); mQueue.push_back(request); @@ -196,7 +216,7 @@ IncidentService::reportIncident(const IncidentReportArgs& args) { ALOGI("reportIncident"); - Status status = checkIncidentPermissions(); + Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } @@ -212,7 +232,7 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args, { ALOGI("reportIncidentToStream"); - Status status = checkIncidentPermissions(); + Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 34930aa57321..bd559d6980f1 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -251,7 +251,7 @@ Reporter::create_file(int* fd) // Override umask. Not super critical. If it fails go on with life. chmod(filename, 0660); - if (chown(filename, AID_SYSTEM, AID_SYSTEM)) { + if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) { ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno)); status_t err = -errno; unlink(mFilename.c_str()); diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 61d16f815e65..0827785811b6 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -19,6 +19,7 @@ #include "Section.h" #include <errno.h> +#include <sys/prctl.h> #include <unistd.h> #include <wait.h> @@ -30,7 +31,6 @@ #include <log/log_event_list.h> #include <log/logprint.h> #include <log/log_read.h> -#include <private/android_filesystem_config.h> // for AID_NOBODY #include <private/android_logger.h> #include "FdBuffer.h" @@ -55,26 +55,20 @@ static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe) { const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL }; - // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - // child process executes incident helper as nobody - if (setgid(AID_NOBODY) == -1) { - ALOGW("%s can't change gid: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); - } - if (setuid(AID_NOBODY) == -1) { - ALOGW("%s can't change uid: %s", name, strerror(errno)); - _exit(EXIT_FAILURE); - } - - if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close() || - dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) { + if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(), STDIN_FILENO)) != 0 + || !p2cPipe.close() + || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1 + || !c2pPipe.close()) { ALOGW("%s can't setup stdin and stdout for incident helper", name); _exit(EXIT_FAILURE); } + /* make sure the child dies when incidentd dies */ + prctl(PR_SET_PDEATHSIG, SIGKILL); + execv(INCIDENT_HELPER, const_cast<char**>(ihArgs)); ALOGW("%s failed in incident helper process: %s", name, strerror(errno)); @@ -87,11 +81,23 @@ fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpi } // ================================================================================ +static status_t statusCode(int status) { + if (WIFSIGNALED(status)) { + ALOGD("return by signal: %s", strerror(WTERMSIG(status))); + return -WTERMSIG(status); + } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { + ALOGD("return by exit: %s", strerror(WEXITSTATUS(status))); + return -WEXITSTATUS(status); + } + return NO_ERROR; +} + static status_t kill_child(pid_t pid) { int status; + ALOGD("try to kill child process %d", pid); kill(pid, SIGKILL); if (waitpid(pid, &status, 0) == -1) return -1; - return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status); + return statusCode(status); } static status_t wait_child(pid_t pid) { @@ -104,7 +110,7 @@ static status_t wait_child(pid_t pid) { nanosleep(&WAIT_INTERVAL_NS, NULL); } if (!died) return kill_child(pid); - return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status); + return statusCode(status); } // ================================================================================ static const Privacy* @@ -275,9 +281,9 @@ FileSection::Execute(ReportRequestSet* requests) const status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(), this->timeoutMs, mIsSysfs); if (readStatus != NO_ERROR || buffer.timedOut()) { - ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false", - strerror(-kill_child(pid))); + ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s", + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + kill_child(pid); return readStatus; } @@ -543,10 +549,10 @@ CommandSection::Execute(ReportRequestSet* requests) const close(cmdPipe.writeFd()); status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs); if (readStatus != NO_ERROR || buffer.timedOut()) { - ALOGW("CommandSection '%s' failed to read data from incident helper: %s, " - "timedout: %s, kill command: %s, kill incident helper: %s", - this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false", - strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid))); + ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s", + this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); + kill_child(cmdPid); + kill_child(ihPid); return readStatus; } diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp index af4a35cc0015..90f543e30ff7 100644 --- a/cmds/incidentd/src/io_util.cpp +++ b/cmds/incidentd/src/io_util.cpp @@ -23,7 +23,7 @@ status_t write_all(int fd, uint8_t const* buf, size_t size) { while (size > 0) { - ssize_t amt = ::write(fd, buf, size); + ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size)); if (amt < 0) { return -errno; } diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp index 65030b3a1799..20111d8ae89a 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -58,26 +58,9 @@ create_directory(const char* directory) goto done; } } else { - if (mkdir(dir, 0770)) { - ALOGE("No incident reports today. " - "Unable to create incident report dir %s: %s", dir, - strerror(errno)); - err = -errno; - goto done; - } - if (chmod(dir, 0770)) { - ALOGE("No incident reports today. " - "Unable to set permissions for incident report dir %s: %s", dir, - strerror(errno)); - err = -errno; - goto done; - } - if (chown(dir, AID_SYSTEM, AID_SYSTEM)) { - ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n", - dir, strerror(errno)); - err = -errno; - goto done; - } + ALOGE("No such directory %s, something wrong.", dir); + err = -1; + goto done; } if (!last) { *d++ = '/'; @@ -97,8 +80,7 @@ create_directory(const char* directory) err = BAD_VALUE; goto done; } - if ((st.st_uid != AID_SYSTEM && st.st_uid != AID_ROOT) || - (st.st_gid != AID_SYSTEM && st.st_gid != AID_ROOT)) { + if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", st.st_uid, st.st_gid, directory); err = BAD_VALUE; diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index 531c9f29bf03..c494bd646b0b 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -17,6 +17,7 @@ #include "Reporter.h" #include <android/os/BnIncidentReportStatusListener.h> +#include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <android-base/file.h> #include <android-base/test_utils.h> @@ -29,6 +30,7 @@ using namespace android; using namespace android::base; using namespace android::binder; +using namespace android::os; using namespace std; using ::testing::StrEq; using ::testing::Test; @@ -141,7 +143,8 @@ TEST_F(ReporterTest, RunReportWithHeaders) { IncidentReportArgs args1, args2; args1.addSection(1); args2.addSection(2); - std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'}; + IncidentHeaderProto header; + header.set_alert_id(12); args2.addHeader(header); sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd); sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd); @@ -153,7 +156,7 @@ TEST_F(ReporterTest, RunReportWithHeaders) { string result; ReadFileToString(tf.path, &result); - EXPECT_THAT(result, StrEq("\n\x5" "abcde")); + EXPECT_THAT(result, StrEq("\n\x2" "\b\f")); EXPECT_EQ(l->startInvoked, 2); EXPECT_EQ(l->finishInvoked, 2); @@ -164,13 +167,16 @@ TEST_F(ReporterTest, RunReportWithHeaders) { TEST_F(ReporterTest, RunReportToGivenDirectory) { IncidentReportArgs args; - args.addHeader({'1', '2', '3'}); - args.addHeader({'a', 'b', 'c', 'd'}); + IncidentHeaderProto header1, header2; + header1.set_alert_id(12); + header2.set_reason("abcd"); + args.addHeader(header1); + args.addHeader(header2); sp<ReportRequest> r = new ReportRequest(args, l, -1); reporter->batch.add(r); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); vector<string> results = InspectFiles(); ASSERT_EQ((int)results.size(), 1); - EXPECT_EQ(results[0], "\n\x3" "123\n\x4" "abcd"); + EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd"); } diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index cbfb89685de0..2cfd7df6be84 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -18,6 +18,7 @@ #include <android-base/file.h> #include <android-base/test_utils.h> +#include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <string.h> @@ -34,6 +35,7 @@ const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 using namespace android::base; using namespace android::binder; +using namespace android::os; using namespace std; using ::testing::StrEq; using ::testing::internal::CaptureStdout; @@ -66,15 +68,9 @@ TEST(SectionTest, HeaderSection) { args1.addSection(2); args2.setAll(true); - vector<uint8_t> head1; - head1.push_back('a'); - head1.push_back('x'); - head1.push_back('e'); - - vector<uint8_t> head2; - head2.push_back('p'); - head2.push_back('u'); - head2.push_back('p'); + IncidentHeaderProto head1, head2; + head1.set_reason("axe"); + head2.set_reason("pup"); args1.addHeader(head1); args1.addHeader(head2); @@ -87,10 +83,10 @@ TEST(SectionTest, HeaderSection) { string content; CaptureStdout(); ASSERT_EQ(NO_ERROR, hs.Execute(&requests)); - EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup")); + EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup")); EXPECT_TRUE(ReadFileToString(output2.path, &content)); - EXPECT_THAT(content, StrEq("\n\x03pup")); + EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); } TEST(SectionTest, FileSection) { diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 2c35d413631b..01f4a84bbb93 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -34,9 +34,10 @@ statsd_common_src := \ src/config/ConfigKey.cpp \ src/config/ConfigListener.cpp \ src/config/ConfigManager.cpp \ + src/external/Perfetto.cpp \ src/external/StatsPuller.cpp \ src/external/StatsCompanionServicePuller.cpp \ - src/external/ResourcePowerManagerPuller.cpp \ + src/external/SubsystemSleepStatePuller.cpp \ src/external/CpuTimePerUidPuller.cpp \ src/external/CpuTimePerUidFreqPuller.cpp \ src/external/StatsPullerManagerImpl.cpp \ @@ -57,6 +58,7 @@ statsd_common_src := \ src/metrics/MetricsManager.cpp \ src/metrics/metrics_manager_util.cpp \ src/packages/UidMap.cpp \ + src/perfetto/perfetto_config.proto \ src/storage/StorageManager.cpp \ src/StatsLogProcessor.cpp \ src/StatsService.cpp \ @@ -134,7 +136,7 @@ LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_INIT_RC := statsd.rc +#LOCAL_INIT_RC := statsd.rc include $(BUILD_EXECUTABLE) @@ -209,6 +211,7 @@ LOCAL_MODULE := statsdprotolite LOCAL_SRC_FILES := \ src/stats_log.proto \ src/statsd_config.proto \ + src/perfetto/perfetto_config.proto \ src/atoms.proto LOCAL_PROTOC_OPTIMIZE_TYPE := lite diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 0ed1c1f11832..ca097d0623ca 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -185,6 +185,7 @@ status_t StatsService::dump(int fd, const Vector<String16>& args) { */ void StatsService::dump_impl(FILE* out) { mConfigManager->Dump(out); + StatsdStats::getInstance().dumpStats(out); } /** @@ -296,9 +297,8 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " NAME The name of the configuration\n"); fprintf(out, "\n"); fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n"); + fprintf(out, "usage: adb shell cmd stats print-stats\n"); fprintf(out, " Prints some basic stats.\n"); - fprintf(out, " reset: 1 to reset the statsd stats. default=0.\n"); } status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { @@ -487,14 +487,8 @@ status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), mProcessor->GetMetricsSize(key)); } - fprintf(out, "Detailed statsd stats in logcat...\n"); StatsdStats& statsdStats = StatsdStats::getInstance(); - bool reset = false; - if (args.size() > 1) { - reset = strtol(args[1].string(), NULL, 10); - } - vector<uint8_t> output; - statsdStats.dumpStats(&output, reset); + statsdStats.dumpStats(out); return NO_ERROR; } diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index f10b2cf618cd..e34aed33a3cb 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,7 +18,9 @@ #include "Log.h" #include "AnomalyTracker.h" +#include "external/Perfetto.h" #include "guardrail/StatsdStats.h" +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" #include <android/os/IIncidentManager.h> #include <android/os/IncidentReportArgs.h> @@ -239,7 +241,7 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { } break; case Subscription::SubscriberInformationCase::kPerfettoDetails: - ALOGW("Perfetto reports not implemented."); + CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details()); break; default: break; @@ -253,10 +255,10 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { for (const auto section : incidentdSections) { incidentReport.addSection(section); } - int64_t alertId = mAlert.id(); - std::vector<uint8_t> header; - uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId)); - header.insert(header.end(), src, src + sizeof(int64_t)); + android::os::IncidentHeaderProto header; + header.set_alert_id(mAlert.id()); + header.mutable_config_key()->set_uid(mConfigKey.GetUid()); + header.mutable_config_key()->set_id(mConfigKey.GetId()); incidentReport.addHeader(header); service->reportIncident(incidentReport); } else { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 4a7f0c4b6545..ef99c9f4d7e3 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -84,6 +84,7 @@ message Atom { AppStartChanged app_start_changed = 48; AppStartCancelChanged app_start_cancel_changed = 49; AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50; + LmkEventOccurred lmk_event_occurred = 51; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } @@ -94,14 +95,18 @@ message Atom { MobileBytesTransfer mobile_bytes_transfer = 10002; MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003; KernelWakelock kernel_wakelock = 10004; - PlatformSleepState platform_sleep_state = 10005; - SleepStateVoter sleep_state_voter = 10006; - SubsystemSleepState subsystem_sleep_state = 10007; + SubsystemSleepState subsystem_sleep_state = 10005; + // 10006 and 10007 are free to use. CpuTimePerFreq cpu_time_per_freq = 10008; CpuTimePerUid cpu_time_per_uid = 10009; CpuTimePerUidFreq cpu_time_per_uid_freq = 10010; WifiActivityEnergyInfo wifi_activity_energy_info = 10011; ModemActivityInfo modem_activity_info = 10012; + MemoryStat memory_stat = 10013; + CpuSuspendTime cpu_suspend_time = 10014; + CpuIdleTime cpu_idle_time = 10015; + CpuActiveTime cpu_active_time = 10016; + CpuClusterTime cpu_cluster_time = 10017; } } @@ -1027,44 +1032,21 @@ message KernelWakelock { } /** - * Pulls PowerStatePlatformSleepState. - * - * Definition here: - * hardware/interfaces/power/1.0/types.hal - */ -message PlatformSleepState { - optional string name = 1; - optional uint64 residency_in_msec_since_boot = 2; - optional uint64 total_transitions = 3; - optional bool supported_only_in_suspend = 4; -} - -/** - * Pulls PowerStateVoter. - * - * Definition here: + * Pulls low power state information. This includes platform and subsystem sleep state information, + * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState as defined in * hardware/interfaces/power/1.0/types.hal - */ -message SleepStateVoter { - optional string platform_sleep_state_name = 1; - optional string voter_name = 2; - optional uint64 total_time_in_msec_voted_for_since_boot = 3; - optional uint64 total_number_of_times_voted_since_boot = 4; -} - -/** - * Pulls PowerStateSubsystemSleepState. - * - * Definition here: * hardware/interfaces/power/1.1/types.hal */ message SubsystemSleepState { - optional string subsystem_name = 1; - optional string subsystem_sleep_state_name = 2; - optional uint64 residency_in_msec_since_boot = 3; - optional uint64 total_transitions = 4; - optional uint64 last_entry_timestamp_ms = 5; - optional bool supported_only_in_suspend = 6; + // Name should be in the format of XXX.YYY where XXX is subsystem name, + // YYY is corresponding voter name. + // If there are no voters, the format should just be XXX (with no dot). + // XXX and YYY should not contain a "." in it. + optional string name = 1; + // The number of times it entered, or voted for entering the sleep state + optional uint64 count = 2; + // The length of time spent in, or spent voting for, the sleep state + optional uint64 timeMs = 3; } /** @@ -1201,3 +1183,91 @@ message ModemActivityInfo { // product of current(mA), voltage(V) and time(ms) optional uint64 energy_used = 10; } + +/* + * Logs the memory stats for a process + */ +message MemoryStat { + // The uid if available. -1 means not available. + optional int32 uid = 1; + + // The app package name. + optional string pkg_name = 2; + + // # of page-faults + optional int64 pgfault = 3; + + // # of major page-faults + optional int64 pgmajfault = 4; + + // RSS+CACHE(+SWAP) + optional int64 usage_in_bytes = 5; +} + +/* + * Logs the event when LMKD kills a process to reduce memory pressure + * Logged from: + * system/core/lmkd/lmkd.c + */ +message LmkEventOccurred { + // The uid if available. -1 means not available. + optional int32 uid = 1; + + // The app package name. + optional string pkg_name = 2; + + // oom adj score. + optional int32 oom_score = 3; + + // Used as start/stop boundaries for the event + enum State { + UNKNOWN = 0; + START = 1; + END = 2; + } + optional State state = 4; +} + +/* + * Cpu syspend time for cpu power calculation. + */ +message CpuSuspendTime { + optional uint64 time = 1; +} + +/* + * Cpu idle time for cpu power calculation. + */ +message CpuIdleTime { + optional uint64 time = 1; +} + +/* + * Reads from /proc/uid_concurrent_active_time which has the format: + * active: X (X is # cores) + * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) + * [uid1]: [time-0] [time-1] [time-2] ... ... + * ... + * Time-N means the CPU time a UID spent running concurrently with N other processes. + * The file contains a monotonically increasing count of time for a single boot. + */ +message CpuActiveTime { + optional uint64 uid = 1; + optional uint64 idx = 2; + optional uint64 time_ms = 3; +} + +/** + * Reads from /proc/uid_concurrent_policy_time which has the format: + * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) + * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * ... + * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. + * The file contains a monotonically increasing count of time for a single boot. + */ +message CpuClusterTime { + optional uint64 uid = 1; + optional uint64 idx = 2; + optional uint64 time_ms = 3; +}
\ No newline at end of file diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 25257213a5d0..7a1bb0c71373 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -19,6 +19,7 @@ #include "SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" +#include "dimension.h" #include <log/logprint.h> @@ -171,12 +172,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou // We get a new output key. newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse; if (matchStart && mInitialValue != ConditionState::kTrue) { - mSlicedConditionState[outputKey] = 1; + mSlicedConditionState.insert(std::make_pair(outputKey, 1)); changed = true; } else if (mInitialValue != ConditionState::kFalse) { // it's a stop and we don't have history about it. // If the default condition is not false, it means this stop is valuable to us. - mSlicedConditionState[outputKey] = 0; + mSlicedConditionState.insert(std::make_pair(outputKey, 0)); changed = true; } } else { @@ -341,7 +342,23 @@ void SimpleConditionTracker::isConditionMet( conditionState = conditionState | (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse); } else { - conditionState = conditionState | mInitialValue; + // For unseen key, check whether the require dimensions are subset of sliced condition + // output. + bool seenDimension = false; + for (const auto& slice : mSlicedConditionState) { + if (IsSubDimension(slice.first.getDimensionsValue(), + key.getDimensionsValue())) { + seenDimension = true; + conditionState = conditionState | + (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse); + } + if (conditionState == ConditionState::kTrue) { + break; + } + } + if (!seenDimension) { + conditionState = conditionState | mInitialValue; + } } } conditionCache[mIndex] = conditionState; diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index a5aee73ce84f..ddfb8d12a335 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -158,15 +158,15 @@ void flattenValueLeaves(const DimensionsValue& value, std::vector<HashableDimensionKey> getDimensionKeysForCondition( const LogEvent& event, const MetricConditionLink& link) { std::vector<Field> whatFields; - getFieldsFromFieldMatcher(link.dimensions_in_what(), &whatFields); + getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields); std::vector<Field> conditionFields; - getFieldsFromFieldMatcher(link.dimensions_in_condition(), &conditionFields); + getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields); std::vector<HashableDimensionKey> hashableDimensionKeys; // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and // directly construct the full condition value tree. - std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.dimensions_in_what()); + std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.fields_in_what()); for (size_t i = 0; i < whatValues.size(); ++i) { std::vector<DimensionsValue> whatLeaves; @@ -185,7 +185,7 @@ std::vector<HashableDimensionKey> getDimensionKeysForCondition( conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j])); } std::vector<DimensionsValue> conditionValues; - findDimensionsValues(conditionValueMap, link.dimensions_in_condition(), &conditionValues); + findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValues); if (conditionValues.size() != 1) { ALOGE("Not able to find unambiguous field value in condition atom."); continue; diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 554ff8a85094..42994b558208 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -255,7 +255,7 @@ StatsdConfig build_fake_config() { metric->set_id(2); // "METRIC_2" metric->set_what(104); metric->set_bucket(ONE_MINUTE); - FieldMatcher* dimensions = metric->mutable_dimensions(); + FieldMatcher* dimensions = metric->mutable_dimensions_in_what(); dimensions->set_field(UID_PROCESS_STATE_TAG_ID); dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY); @@ -278,7 +278,7 @@ StatsdConfig build_fake_config() { metric->set_what(104); metric->set_bucket(ONE_MINUTE); - dimensions = metric->mutable_dimensions(); + dimensions = metric->mutable_dimensions_in_what(); dimensions->set_field(UID_PROCESS_STATE_TAG_ID); dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY); metric->set_condition(202); @@ -288,7 +288,7 @@ StatsdConfig build_fake_config() { metric->set_id(4); metric->set_what(107); metric->set_bucket(ONE_MINUTE); - dimensions = metric->mutable_dimensions(); + dimensions = metric->mutable_dimensions_in_what(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); @@ -296,44 +296,44 @@ StatsdConfig build_fake_config() { metric->set_condition(204); MetricConditionLink* link = metric->add_links(); link->set_condition(203); - link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); - link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); - link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); + link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // Duration of an app holding any wl, while screen on and app in background, slice by uid DurationMetric* durationMetric = config.add_duration_metric(); durationMetric->set_id(5); durationMetric->set_bucket(ONE_MINUTE); durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - dimensions = durationMetric->mutable_dimensions(); + dimensions = durationMetric->mutable_dimensions_in_what(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what(205); durationMetric->set_condition(204); link = durationMetric->add_links(); link->set_condition(203); - link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); - link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); - link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); + link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // max Duration of an app holding any wl, while screen on and app in background, slice by uid durationMetric = config.add_duration_metric(); durationMetric->set_id(6); durationMetric->set_bucket(ONE_MINUTE); durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); - dimensions = durationMetric->mutable_dimensions(); + dimensions = durationMetric->mutable_dimensions_in_what(); dimensions->set_field(WAKE_LOCK_TAG_ID); dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what(205); durationMetric->set_condition(204); link = durationMetric->add_links(); link->set_condition(203); - link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); - link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); - link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); + link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // Duration of an app holding any wl, while screen on and app in background durationMetric = config.add_duration_metric(); @@ -344,10 +344,10 @@ StatsdConfig build_fake_config() { durationMetric->set_condition(204); link = durationMetric->add_links(); link->set_condition(203); - link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); - link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); - link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); - link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); + link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // Duration of screen on time. @@ -376,7 +376,7 @@ StatsdConfig build_fake_config() { valueMetric->mutable_value_field()->set_field(KERNEL_WAKELOCK_TAG_ID); valueMetric->mutable_value_field()->add_child()->set_field(KERNEL_WAKELOCK_COUNT_KEY); valueMetric->set_condition(201); - dimensions = valueMetric->mutable_dimensions(); + dimensions = valueMetric->mutable_dimensions_in_what(); dimensions->set_field(KERNEL_WAKELOCK_TAG_ID); dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY); // This is for testing easier. We should never set bucket size this small. diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp index 09499b6406e7..bb7a044fa8ce 100644 --- a/cmds/statsd/src/dimension.cpp +++ b/cmds/statsd/src/dimension.cpp @@ -331,13 +331,15 @@ bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub case DimensionsValue::ValueCase::kValueFloat: return dimension.value_float() == sub.value_float(); case DimensionsValue::ValueCase::kValueTuple: { - if (dimension.value_tuple().dimensions_value_size() < sub.value_tuple().dimensions_value_size()) { + if (dimension.value_tuple().dimensions_value_size() < + sub.value_tuple().dimensions_value_size()) { return false; } bool allSub = true; - for (int i = 0; i < sub.value_tuple().dimensions_value_size(); ++i) { + for (int i = 0; allSub && i < sub.value_tuple().dimensions_value_size(); ++i) { bool isSub = false; - for (int j = 0; !isSub && j < dimension.value_tuple().dimensions_value_size(); ++j) { + for (int j = 0; !isSub && + j < dimension.value_tuple().dimensions_value_size(); ++j) { isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j), sub.value_tuple().dimensions_value(i)); } diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp index a61afb429f54..a75127324745 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -34,7 +34,7 @@ namespace android { namespace os { namespace statsd { -static const string sProcFile = "/proc/uid_cputime/show_uid_stat"; +static const string sProcFile = "/proc/uid_time_in_state"; static const int kLineBufferSize = 1024; /** diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp new file mode 100644 index 000000000000..7a2d1991a538 --- /dev/null +++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp @@ -0,0 +1,89 @@ +/* + * 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 DEBUG true // STOPSHIP if true +#include "Log.h" + +#include <fstream> + +#include "KernelUidCpuActiveTimeReader.h" +#include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" +#include "statslog.h" + +using std::make_shared; +using std::shared_ptr; +using std::ifstream; + +namespace android { +namespace os { +namespace statsd { + +static const string sProcFile = "/proc/uid_concurrent_active_time"; +static const int kLineBufferSize = 1024; + +/** + * Reads /proc/uid_concurrent_active_time which has the format: + * active: X (X is # cores) + * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) + * [uid1]: [time-0] [time-1] [time-2] ... ... + * ... + * Time-N means the CPU time a UID spent running concurrently with N other processes. + * The file contains a monotonically increasing count of time for a single boot. + */ +KernelUidCpuActiveTimeReader::KernelUidCpuActiveTimeReader() : StatsPuller(android::util::CPU_ACTIVE_TIME) { +} + +bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) { + data->clear(); + + ifstream fin; + fin.open(sProcFile); + if (!fin.good()) { + VLOG("Failed to read pseudo file %s", sProcFile.c_str()); + return false; + } + + uint64_t timestamp = time(nullptr) * NS_PER_SEC; + char buf[kLineBufferSize]; + char* pch; + while (!fin.eof()) { + fin.getline(buf, kLineBufferSize); + pch = strtok(buf, " :"); + if (pch == NULL) break; + uint64_t uid = std::stoull(pch); + pch = strtok(NULL, " "); + uint64_t timeMs; + int idx = 0; + do { + timeMs = std::stoull(pch); + auto ptr = make_shared<LogEvent>(mTagId, timestamp); + ptr->write(uid); + ptr->write(idx); + ptr->write(timeMs); + ptr->init(); + data->push_back(ptr); + VLOG("uid %lld, freq idx %d, active time %lld", (long long)uid, idx, (long long)timeMs); + idx++; + pch = strtok(NULL, " "); + } while (pch != NULL); + } + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h new file mode 100644 index 000000000000..fcae35fa6eb5 --- /dev/null +++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#pragma once + +#include <utils/String16.h> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +class KernelUidCpuActiveTimeReader : public StatsPuller { + public: + KernelUidCpuActiveTimeReader(); + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp new file mode 100644 index 000000000000..7426e743c40f --- /dev/null +++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp @@ -0,0 +1,88 @@ +/* + * 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 DEBUG true // STOPSHIP if true +#include "Log.h" + +#include <fstream> +#include "KernelUidCpuClusterTimeReader.h" +#include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" +#include "statslog.h" + +using std::make_shared; +using std::shared_ptr; +using std::ifstream; + +namespace android { +namespace os { +namespace statsd { + +static const string sProcFile = "/proc/uid_concurrent_policy_time"; +static const int kLineBufferSize = 1024; + +/** + * Reads /proc/uid_concurrent_policy_time which has the format: + * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) + * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * ... + * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. + * The file contains a monotonically increasing count of time for a single boot. + */ +KernelUidCpuClusterTimeReader::KernelUidCpuClusterTimeReader() : StatsPuller(android::util::CPU_CLUSTER_TIME) { +} + +bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) { + data->clear(); + + ifstream fin; + fin.open(sProcFile); + if (!fin.good()) { + VLOG("Failed to read pseudo file %s", sProcFile.c_str()); + return false; + } + + uint64_t timestamp = time(nullptr) * NS_PER_SEC; + char buf[kLineBufferSize]; + char* pch; + while (!fin.eof()) { + fin.getline(buf, kLineBufferSize); + pch = strtok(buf, " :"); + if (pch == NULL) break; + uint64_t uid = std::stoull(pch); + pch = strtok(NULL, " "); + uint64_t timeMs; + int idx = 0; + do { + timeMs = std::stoull(pch); + auto ptr = make_shared<LogEvent>(mTagId, timestamp); + ptr->write(uid); + ptr->write(idx); + ptr->write(timeMs); + ptr->init(); + data->push_back(ptr); + VLOG("uid %lld, freq idx %d, cluster time %lld", (long long)uid, idx, (long long)timeMs); + idx++; + pch = strtok(NULL, " "); + } while (pch != NULL); + } + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h new file mode 100644 index 000000000000..90236ae00762 --- /dev/null +++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#pragma once + +#include <utils/String16.h> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Reads /proc/uid_cputime/show_uid_stat which has the line format: + * + * uid: user_time_micro_seconds system_time_micro_seconds + * + * This provides the time a UID's processes spent executing in user-space and kernel-space. + * The file contains a monotonically increasing count of time for a single boot. + */ +class KernelUidCpuClusterTimeReader : public StatsPuller { + public: + KernelUidCpuClusterTimeReader(); + bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp new file mode 100644 index 000000000000..f7b33e77c557 --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include "Log.h" + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert + +#include <android-base/unique_fd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +namespace { +const char kDropboxTag[] = "perfetto"; +} + +namespace android { +namespace os { +namespace statsd { + +bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) { + ALOGD("Starting trace collection through perfetto"); + + if (!config.has_trace_config()) { + ALOGE("The perfetto trace config is empty, aborting"); + return false; + } + + android::base::unique_fd readPipe; + android::base::unique_fd writePipe; + if (!android::base::Pipe(&readPipe, &writePipe)) { + ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + pid_t pid = fork(); + if (pid < 0) { + ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + if (pid == 0) { + // Child process. + + // No malloc calls or library calls after this point. Remember that even + // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf(). + + writePipe.reset(); // Close the write end (owned by the main process). + + // Replace stdin with |readPipe| so the main process can write into it. + if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1); + execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox", + kDropboxTag, nullptr); + + // execl() doesn't return in case of success, if we get here something failed. + _exit(1); + } + + // Main process. + + readPipe.reset(); // Close the read end (owned by the child process). + + // Using fopen() because fwrite() has the right logic to chunking write() + // over a pipe (see __sfvwrite()). + FILE* writePipeStream = fdopen(writePipe.get(), "wb"); + if (!writePipeStream) { + ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno)); + return false; + } + + std::string cfgProto = config.trace_config().SerializeAsString(); + size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream); + fclose(writePipeStream); + if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) { + ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten, + strerror(errno)); + return false; + } + + // This does NOT wait for the full duration of the trace. It just waits until the process + // has read the config from stdin and detached. + int childStatus = 0; + waitpid(pid, &childStatus, 0); + if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) { + ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus); + return false; + } + + ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded"); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h new file mode 100644 index 000000000000..e2e02533f17f --- /dev/null +++ b/cmds/statsd/src/external/Perfetto.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#pragma once + +using android::os::StatsLogEventWrapper; + +namespace android { +namespace os { +namespace statsd { + +class PerfettoDetails; // Declared in statsd_config.pb.h + +// Starts the collection of a Perfetto trace with the given |config|. +// The trace is uploaded to Dropbox by the perfetto cmdline util once done. +// This method returns immediately after passing the config and does NOT wait +// for the full duration of the trace. +bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 58c7b126a5a5..79f1a5d84174 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -23,7 +23,7 @@ #include <climits> #include "CpuTimePerUidFreqPuller.h" #include "CpuTimePerUidPuller.h" -#include "ResourcePowerManagerPuller.h" +#include "SubsystemSleepStatePuller.h" #include "StatsCompanionServicePuller.h" #include "StatsPullerManagerImpl.h" #include "StatsService.h" @@ -58,16 +58,14 @@ StatsPullerManagerImpl::StatsPullerManagerImpl() mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, make_shared<StatsCompanionServicePuller>( android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}); - mPullers.insert({android::util::PLATFORM_SLEEP_STATE, - make_shared<ResourcePowerManagerPuller>(android::util::PLATFORM_SLEEP_STATE)}); - mPullers.insert({android::util::SLEEP_STATE_VOTER, - make_shared<ResourcePowerManagerPuller>(android::util::SLEEP_STATE_VOTER)}); mPullers.insert( {android::util::SUBSYSTEM_SLEEP_STATE, - make_shared<ResourcePowerManagerPuller>(android::util::SUBSYSTEM_SLEEP_STATE)}); + make_shared<SubsystemSleepStatePuller>()}); mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)}); mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()}); mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()}); + mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)}); + mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)}); mStatsCompanionService = StatsService::getStatsCompanionService(); } diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp index 2e29fb04abfa..6d12e257df8d 100644 --- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp @@ -30,10 +30,10 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> -#include "external/ResourcePowerManagerPuller.h" +#include "external/SubsystemSleepStatePuller.h" #include "external/StatsPuller.h" -#include "ResourcePowerManagerPuller.h" +#include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" @@ -73,10 +73,10 @@ bool getPowerHal() { return gPowerHalV1_0 != nullptr; } -ResourcePowerManagerPuller::ResourcePowerManagerPuller(int tagId) : StatsPuller(tagId) { +SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) { } -bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { +bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { std::lock_guard<std::mutex> lock(gPowerHalMutex); if (!getPowerHal()) { @@ -89,8 +89,6 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data data->clear(); Return<void> ret; - if (mTagId == android::util::PLATFORM_SLEEP_STATE || - mTagId == android::util::SLEEP_STATE_VOTER) { ret = gPowerHalV1_0->getPlatformLowPowerStats( [&data, timestamp](hidl_vec<PowerStatePlatformSleepState> states, Status status) { if (status != Status::SUCCESS) return; @@ -98,12 +96,11 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data for (size_t i = 0; i < states.size(); i++) { const PowerStatePlatformSleepState& state = states[i]; - auto statePtr = make_shared<LogEvent>(android::util::PLATFORM_SLEEP_STATE, + auto statePtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE, timestamp); statePtr->write(state.name); - statePtr->write(state.residencyInMsecSinceBoot); statePtr->write(state.totalTransitions); - statePtr->write(state.supportedOnlyInSuspend); + statePtr->write(state.residencyInMsecSinceBoot); statePtr->init(); data->push_back(statePtr); VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(), @@ -111,12 +108,11 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0); for (auto voter : state.voters) { - auto voterPtr = make_shared<LogEvent>(android::util::SLEEP_STATE_VOTER, + auto voterPtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE, timestamp); - voterPtr->write(state.name); - voterPtr->write(voter.name); - voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot); + voterPtr->write((std::string)state.name + "." + (std::string)voter.name); voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot); + voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot); voterPtr->init(); data->push_back(voterPtr); VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(), @@ -131,9 +127,7 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data gPowerHalV1_0 = nullptr; return false; } - } - if (mTagId == android::util::SUBSYSTEM_SLEEP_STATE) { // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1 sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0); @@ -149,13 +143,10 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data const PowerStateSubsystemSleepState& state = subsystem.states[j]; auto subsystemStatePtr = make_shared<LogEvent>( - android::util::SUBSYSTEM_SLEEP_STATE, timestamp); - subsystemStatePtr->write(subsystem.name); - subsystemStatePtr->write(state.name); - subsystemStatePtr->write(state.residencyInMsecSinceBoot); + android::util::SUBSYSTEM_SLEEP_STATE, timestamp); + subsystemStatePtr->write((std::string)subsystem.name + "." + (std::string)state.name); subsystemStatePtr->write(state.totalTransitions); - subsystemStatePtr->write(state.lastEntryTimestampMs); - subsystemStatePtr->write(state.supportedOnlyInSuspend); + subsystemStatePtr->write(state.residencyInMsecSinceBoot); subsystemStatePtr->init(); data->push_back(subsystemStatePtr); VLOG("subsystemstate: %s, %s, %lld, %lld, %lld", @@ -168,7 +159,6 @@ bool ResourcePowerManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data } }); } - } return true; } diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h index 339940839c4d..17ce5b4cb918 100644 --- a/cmds/statsd/src/external/ResourcePowerManagerPuller.h +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.h @@ -26,9 +26,9 @@ namespace statsd { /** * Reads hal for sleep states */ -class ResourcePowerManagerPuller : public StatsPuller { +class SubsystemSleepStatePuller : public StatsPuller { public: - ResourcePowerManagerPuller(int tagId); + SubsystemSleepStatePuller(); bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override; }; diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 36dd6167663b..63bde7d82372 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -66,8 +66,6 @@ std::map<int, long> StatsdStats::kPullerCooldownMap = { {android::util::MOBILE_BYTES_TRANSFER, 1}, {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, 1}, {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG, 1}, - {android::util::PLATFORM_SLEEP_STATE, 1}, - {android::util::SLEEP_STATE_VOTER, 1}, {android::util::SUBSYSTEM_SLEEP_STATE, 1}, {android::util::CPU_TIME_PER_FREQ, 1}, {android::util::CPU_TIME_PER_UID, 1}, @@ -357,73 +355,135 @@ void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, } } -void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { +void StatsdStats::dumpStats(FILE* out) const { lock_guard<std::mutex> lock(mLock); + time_t t = mStartTimeSec; + struct tm* tm = localtime(&t); + char timeBuffer[80]; + strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm); + fprintf(out, "Stats collection start second: %s\n", timeBuffer); + fprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size()); + for (const auto& configStats : mIceBox) { + fprintf(out, + "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + "#matcher=%d, #alert=%d, valid=%d\n", + configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), + configStats.deletion_time_sec(), configStats.metric_count(), + configStats.condition_count(), configStats.matcher_count(), + configStats.alert_count(), configStats.is_valid()); + + for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + fprintf(out, "\tbroadcast time: %d\n", broadcastTime); + } + + for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + fprintf(out, "\tdata drop time: %d\n", dataDropTime); + } + } + fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); + for (auto& pair : mConfigStats) { + auto& key = pair.first; + auto& configStats = pair.second; + + fprintf(out, + "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + "#matcher=%d, #alert=%d, valid=%d\n", + configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), + configStats.deletion_time_sec(), configStats.metric_count(), + configStats.condition_count(), configStats.matcher_count(), + configStats.alert_count(), configStats.is_valid()); + for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + fprintf(out, "\tbroadcast time: %d\n", broadcastTime); + } + + for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + fprintf(out, "\tdata drop time: %d\n", dataDropTime); + } + + for (const auto& dumpTime : configStats.dump_report_time_sec()) { + fprintf(out, "\tdump report time: %d\n", dumpTime); + } + + // Add matcher stats + auto matcherIt = mMatcherStats.find(key); + if (matcherIt != mMatcherStats.end()) { + const auto& matcherStats = matcherIt->second; + for (const auto& stats : matcherStats) { + fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, + stats.second); + } + } + // Add condition stats + auto conditionIt = mConditionStats.find(key); + if (conditionIt != mConditionStats.end()) { + const auto& conditionStats = conditionIt->second; + for (const auto& stats : conditionStats) { + fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, + stats.second); + } + } + // Add metrics stats + auto metricIt = mMetricsStats.find(key); + if (metricIt != mMetricsStats.end()) { + const auto& conditionStats = metricIt->second; + for (const auto& stats : conditionStats) { + fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, + stats.second); + } + } + // Add anomaly detection alert stats + auto alertIt = mAlertStats.find(key); + if (alertIt != mAlertStats.end()) { + const auto& alertStats = alertIt->second; + for (const auto& stats : alertStats) { + fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, + stats.second); + } + } + } + fprintf(out, "********Pushed Atom stats***********\n"); + const size_t atomCounts = mPushedAtomStats.size(); + for (size_t i = 2; i < atomCounts; i++) { + if (mPushedAtomStats[i] > 0) { + fprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); + } + } - if (DEBUG) { - time_t t = mStartTimeSec; - struct tm* tm = localtime(&t); - char timeBuffer[80]; - strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm); - VLOG("=================StatsdStats dump begins===================="); - VLOG("Stats collection start second: %s", timeBuffer); + fprintf(out, "********Pulled Atom stats***********\n"); + for (const auto& pair : mPulledAtomStats) { + fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull, + (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); + } + + if (mAnomalyAlarmRegisteredStats > 0) { + fprintf(out, "********AnomalyAlarmStats stats***********\n"); + fprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats); } + + fprintf(out, + "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " + "lost=%d\n", + mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(), + mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes()); +} + +void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { + lock_guard<std::mutex> lock(mLock); + ProtoOutputStream proto; proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr)); - VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size()); for (const auto& configStats : mIceBox) { const int numBytes = configStats.ByteSize(); vector<char> buffer(numBytes); configStats.SerializeToArray(&buffer[0], numBytes); proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], buffer.size()); - - // surround the whole block with DEBUG, so that compiler can strip out the code - // in production. - if (DEBUG) { - VLOG("*****ICEBOX*****"); - VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, #valid=%d", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); - - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { - VLOG("\tbroadcast time: %d", broadcastTime); - } - - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { - VLOG("\tdata drop time: %d", dataDropTime); - } - } } for (auto& pair : mConfigStats) { auto& configStats = pair.second; - if (DEBUG) { - VLOG("********Active Configs***********"); - VLOG("Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " - "#matcher=%d, #alert=%d, #valid=%d", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { - VLOG("\tbroadcast time: %d", broadcastTime); - } - - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { - VLOG("\tdata drop time: %d", dataDropTime); - } - - for (const auto& dumpTime : configStats.dump_report_time_sec()) { - VLOG("\tdump report time: %d", dumpTime); - } - } - addSubStatsToConfigLocked(pair.first, configStats); const int numBytes = configStats.ByteSize(); @@ -439,7 +499,6 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { configStats.clear_alert_stats(); } - VLOG("********Pushed Atom stats***********"); const size_t atomCounts = mPushedAtomStats.size(); for (size_t i = 2; i < atomCounts; i++) { if (mPushedAtomStats[i] > 0) { @@ -448,34 +507,24 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); proto.end(token); - - VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); } } - VLOG("********Pulled Atom stats***********"); for (const auto& pair : mPulledAtomStats) { android::os::statsd::writePullerStatsToStream(pair, &proto); - VLOG("Atom %d->%ld, %ld, %ld\n", (int) pair.first, (long) pair.second.totalPull, (long) pair.second.totalPullFromCache, (long) pair.second.minPullIntervalSec); } if (mAnomalyAlarmRegisteredStats > 0) { - VLOG("********AnomalyAlarmStats stats***********"); long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED, mAnomalyAlarmRegisteredStats); proto.end(token); - VLOG("Anomaly alarm registrations: %d", mAnomalyAlarmRegisteredStats); } const int numBytes = mUidMapStats.ByteSize(); vector<char> buffer(numBytes); mUidMapStats.SerializeToArray(&buffer[0], numBytes); proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size()); - VLOG("UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " - "lost=%d", - mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(), - mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes()); output->clear(); size_t bufferSize = proto.size(); @@ -495,7 +544,6 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { } VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize); - VLOG("=================StatsdStats dump ends===================="); } } // namespace statsd diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 52ab253b6721..9178daa4fe12 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -189,6 +189,11 @@ public: */ void dumpStats(std::vector<uint8_t>* buffer, bool reset); + /** + * Output statsd stats in human readable format to [out] file. + */ + void dumpStats(FILE* out) const; + typedef struct { long totalPull; long totalPullFromCache; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 3e98098ad62a..ef272103a1b6 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -50,8 +50,9 @@ const int FIELD_ID_COUNT_METRICS = 5; // for CountMetricDataWrapper const int FIELD_ID_DATA = 1; // for CountMetricData -const int FIELD_ID_DIMENSION = 1; -const int FIELD_ID_BUCKET_INFO = 2; +const int FIELD_ID_DIMENSION_IN_WHAT = 1; +const int FIELD_ID_DIMENSION_IN_CONDITION = 2; +const int FIELD_ID_BUCKET_INFO = 3; // for CountBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -70,7 +71,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric } // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions(); + mDimensions = metric.dimensions_in_what(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -98,7 +99,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog auto count_metrics = report->mutable_count_metrics(); for (const auto& counter : mPastBuckets) { CountMetricData* metricData = count_metrics->add_data(); - *metricData->mutable_dimension() = counter.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue(); for (const auto& bucket : counter.second) { CountBucketInfo* bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -127,7 +128,7 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index b54629780318..58dd46400a9f 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -49,8 +49,9 @@ const int FIELD_ID_DURATION_METRICS = 6; // for DurationMetricDataWrapper const int FIELD_ID_DATA = 1; // for DurationMetricData -const int FIELD_ID_DIMENSION = 1; -const int FIELD_ID_BUCKET_INFO = 2; +const int FIELD_ID_DIMENSION_IN_WHAT = 1; +const int FIELD_ID_DIMENSION_IN_CONDITION = 2; +const int FIELD_ID_BUCKET_INFO = 3; // for DurationBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -80,7 +81,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat } // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions(); + mDimensions = metric.dimensions_in_what(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -154,7 +155,7 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, Stats auto duration_metrics = report->mutable_duration_metrics(); for (const auto& pair : mPastBuckets) { DurationMetricData* metricData = duration_metrics->add_data(); - *metricData->mutable_dimension() = pair.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue(); for (const auto& bucket : pair.second) { auto bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -183,7 +184,7 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index ae47bd898cf9..17305e3857b6 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -51,8 +51,9 @@ const int FIELD_ID_GAUGE_METRICS = 8; // for GaugeMetricDataWrapper const int FIELD_ID_DATA = 1; // for GaugeMetricData -const int FIELD_ID_DIMENSION = 1; -const int FIELD_ID_BUCKET_INFO = 2; +const int FIELD_ID_DIMENSION_IN_WHAT = 1; +const int FIELD_ID_DIMENSION_IN_CONDITION = 2; +const int FIELD_ID_BUCKET_INFO = 3; // for GaugeBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -79,7 +80,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mFieldFilter = metric.gauge_fields_filter(); // TODO: use UidMap if uid->pkg_name is required - mDimensions = metric.dimensions(); + mDimensions = metric.dimensions_in_what(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -138,7 +139,7 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 74bd6f97d535..e98587356857 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -54,8 +54,9 @@ const int FIELD_ID_VALUE_METRICS = 7; // for ValueMetricDataWrapper const int FIELD_ID_DATA = 1; // for ValueMetricData -const int FIELD_ID_DIMENSION = 1; -const int FIELD_ID_BUCKET_INFO = 2; +const int FIELD_ID_DIMENSION_IN_WHAT = 1; +const int FIELD_ID_DIMENSION_IN_CONDITION = 2; +const int FIELD_ID_BUCKET_INFO = 3; // for ValueBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -80,7 +81,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric } mBucketSizeNs = bucketSizeMills * 1000000; - mDimensions = metric.dimensions(); + mDimensions = metric.dimensions_in_what(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -123,7 +124,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLog auto value_metrics = report->mutable_value_metrics(); for (const auto& pair : mPastBuckets) { ValueMetricData* metricData = value_metrics->add_data(); - *metricData->mutable_dimension() = pair.first.getDimensionsValue(); + *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue(); for (const auto& bucket : pair.second) { ValueBucketInfo* bucketInfo = metricData->add_bucket_info(); bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); @@ -149,7 +150,7 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, // First fill dimension. long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT); writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); protoOutput->end(dimensionToken); @@ -284,8 +285,15 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( interval.start = value; interval.startUpdated = true; } else { + // Generally we expect value to be monotonically increasing. + // If not, there was a reset event. We take the absolute value as + // diff in this case. if (interval.startUpdated) { - interval.sum += (value - interval.start); + if (value > interval.start) { + interval.sum += (value - interval.start); + } else { + interval.sum += value; + } interval.startUpdated = false; } else { VLOG("No start for matching end %ld", value); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index bc887ac0ba8f..a0173ee9922f 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -227,7 +227,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; @@ -279,7 +280,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int trackerIndices[3] = {-1, -1, -1}; if (!simplePredicate.has_start() || !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions(), allAtomMatchers, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[0])) { ALOGE("Duration metrics must specify a valid the start event matcher"); return false; @@ -287,14 +288,14 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti if (simplePredicate.has_stop() && !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions(), allAtomMatchers, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[1])) { return false; } if (simplePredicate.has_stop_all() && !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions(), allAtomMatchers, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[2])) { return false; } @@ -371,7 +372,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; @@ -429,7 +431,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.id(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index b0c31975c91c..eefb7dc046ec 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -106,9 +106,9 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, t->set_uid(uid[j]); } mBytesUsed += snapshot->ByteSize(); + ensureBytesUsedBelowLimit(); StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); StatsdStats::getInstance().setUidMapSnapshots(mOutput.snapshots_size()); - ensureBytesUsedBelowLimit(); getListenerListCopyLocked(&broadcastList); } // To avoid invoking callback while holding the internal lock. we get a copy of the listener @@ -142,9 +142,9 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i log->set_uid(uid); log->set_version(versionCode); mBytesUsed += log->ByteSize(); + ensureBytesUsedBelowLimit(); StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); - ensureBytesUsedBelowLimit(); auto range = mMap.equal_range(int(uid)); bool found = false; @@ -222,9 +222,9 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i log->set_app(app); log->set_uid(uid); mBytesUsed += log->ByteSize(); + ensureBytesUsedBelowLimit(); StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); StatsdStats::getInstance().setUidMapChanges(mOutput.changes_size()); - ensureBytesUsedBelowLimit(); auto range = mMap.equal_range(int(uid)); for (auto it = range.first; it != range.second; ++it) { @@ -471,10 +471,13 @@ const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0}, {"AID_AUTOMOTIVE_EVS", 1062}, {"AID_LOWPAN", 1063}, {"AID_HSM", 1064}, + {"AID_RESERVED_DISK", 1065}, + {"AID_STATSD", 1066}, + {"AID_INCIDENTD", 1067}, {"AID_SHELL", 2000}, {"AID_CACHE", 2001}, {"AID_DIAG", 2002}}; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index 4e37977dbf3b..3304f6c976aa 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -152,9 +152,9 @@ private: // until the memory consumed by mOutput is below the specified limit. void ensureBytesUsedBelowLimit(); - // Override used for testing the max memory allowed by uid map. -1 means we use the value + // Override used for testing the max memory allowed by uid map. 0 means we use the value // specified in StatsdStats.h with the rest of the guardrails. - size_t maxBytesOverride = -1; + size_t maxBytesOverride = 0; // Cache the size of mOutput; size_t mBytesUsed; diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto new file mode 100644 index 000000000000..dc868f997a63 --- /dev/null +++ b/cmds/statsd/src/perfetto/perfetto_config.proto @@ -0,0 +1,61 @@ +/* + * 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. + */ + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +package perfetto.protos; + +message DataSourceConfig { + message FtraceConfig { + repeated string event_names = 1; + } + + optional string name = 1; + + optional uint32 target_buffer = 2; + + optional FtraceConfig ftrace_config = 100; +} + +message TraceConfig { + message BufferConfig { + optional uint32 size_kb = 1; + + enum OptimizeFor { + DEFAULT = 0; + ONE_SHOT_READ = 1; + + } + optional OptimizeFor optimize_for = 3; + + enum FillPolicy { + UNSPECIFIED = 0; + RING_BUFFER = 1; + } + optional FillPolicy fill_policy = 4; + } + repeated BufferConfig buffers = 1; + + message DataSource { + optional protos.DataSourceConfig config = 1; + + repeated string producer_name_filter = 2; + } + repeated DataSource data_sources = 2; + + optional uint32 duration_ms = 3; +} diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 2596a5f4fdc4..bb2193fb442b 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -56,9 +56,11 @@ message CountBucketInfo { } message CountMetricData { - optional DimensionsValue dimension = 1; + optional DimensionsValue dimensions_in_what = 1; - repeated CountBucketInfo bucket_info = 2; + optional DimensionsValue dimensions_in_condition = 2; + + repeated CountBucketInfo bucket_info = 3; } message DurationBucketInfo { @@ -70,9 +72,11 @@ message DurationBucketInfo { } message DurationMetricData { - optional DimensionsValue dimension = 1; + optional DimensionsValue dimensions_in_what = 1; + + optional DimensionsValue dimensions_in_condition = 2; - repeated DurationBucketInfo bucket_info = 2; + repeated DurationBucketInfo bucket_info = 3; } message ValueBucketInfo { @@ -84,9 +88,11 @@ message ValueBucketInfo { } message ValueMetricData { - optional DimensionsValue dimension = 1; + optional DimensionsValue dimensions_in_what = 1; - repeated ValueBucketInfo bucket_info = 2; + optional DimensionsValue dimensions_in_condition = 2; + + repeated ValueBucketInfo bucket_info = 3; } message GaugeBucketInfo { @@ -98,9 +104,11 @@ message GaugeBucketInfo { } message GaugeMetricData { - optional DimensionsValue dimension = 1; + optional DimensionsValue dimensions_in_what = 1; + + optional DimensionsValue dimensions_in_condition = 2; - repeated GaugeBucketInfo bucket_info = 2; + repeated GaugeBucketInfo bucket_info = 3; } message UidMapping { diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index d45a6b038ccb..cd60ee74e3ad 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -22,6 +22,8 @@ package android.os.statsd; option java_package = "com.android.internal.os"; option java_outer_classname = "StatsdConfigProto"; +import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto"; + enum Position { POSITION_UNKNOWN = 0; FIRST = 1; @@ -151,9 +153,9 @@ message Bucket { message MetricConditionLink { optional int64 condition = 1; - optional FieldMatcher dimensions_in_what = 2; + optional FieldMatcher fields_in_what = 2; - optional FieldMatcher dimensions_in_condition = 3; + optional FieldMatcher fields_in_condition = 3; } message FieldFilter { @@ -178,7 +180,9 @@ message CountMetric { optional int64 condition = 3; - optional FieldMatcher dimensions = 4; + optional FieldMatcher dimensions_in_what = 4; + + optional FieldMatcher dimensions_in_condition = 7; optional TimeUnit bucket = 5; @@ -201,7 +205,9 @@ message DurationMetric { } optional AggregationType aggregation_type = 5 [default = SUM]; - optional FieldMatcher dimensions = 6; + optional FieldMatcher dimensions_in_what = 6; + + optional FieldMatcher dimensions_in_condition = 8; optional TimeUnit bucket = 7; } @@ -215,7 +221,9 @@ message GaugeMetric { optional int64 condition = 4; - optional FieldMatcher dimensions = 5; + optional FieldMatcher dimensions_in_what = 5; + + optional FieldMatcher dimensions_in_condition = 8; optional TimeUnit bucket = 6; @@ -231,7 +239,9 @@ message ValueMetric { optional int64 condition = 4; - optional FieldMatcher dimensions = 5; + optional FieldMatcher dimensions_in_what = 5; + + optional FieldMatcher dimensions_in_condition = 9; optional TimeUnit bucket = 6; @@ -264,7 +274,7 @@ message IncidentdDetails { } message PerfettoDetails { - optional int32 perfetto_stuff = 1; + optional perfetto.protos.TraceConfig trace_config = 1; } message Subscription { diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index c542db2312ea..1d75e20d7eaa 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -111,20 +111,21 @@ void StorageManager::sendBroadcast(const char* path, int index = 0; int uid = 0; - string configName; + int64_t configID = 0; char* substr = strtok(name, "-"); // Timestamp lives at index 2 but we skip parsing it as it's not needed. while (substr != nullptr && index < 2) { - if (index) { + if (index == 0) { uid = atoi(substr); - } else { - configName = substr; + } else if (index == 1) { + configID = StrToInt64(substr); } index++; + substr = strtok(nullptr, "-"); } if (index < 2) continue; - sendBroadcast(ConfigKey(uid, StrToInt64(configName))); + sendBroadcast(ConfigKey(uid, configID)); } } @@ -143,19 +144,23 @@ void StorageManager::appendConfigMetricsReport(const char* path, ProtoOutputStre int index = 0; int uid = 0; - string configName; + int64_t configID = 0; + int64_t timestamp = 0; char* substr = strtok(name, "-"); - // Timestamp lives at index 2 but we skip parsing it as it's not needed. - while (substr != nullptr && index < 2) { - if (index) { + while (substr != nullptr && index < 3) { + if (index == 0) { uid = atoi(substr); - } else { - configName = substr; + } else if (index == 1) { + configID = StrToInt64(substr); + } else if (index == 2) { + timestamp = atoi(substr); } index++; + substr = strtok(nullptr, "-"); } - if (index < 2) continue; - string file_name = StringPrintf("%s/%s", path, name); + if (index < 3) continue; + string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid, + (long long)configID, (long long)timestamp); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); if (fd != -1) { string content; @@ -186,29 +191,32 @@ void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap int index = 0; int uid = 0; - string configName; + int64_t configID = 0; + int64_t timestamp = 0; char* substr = strtok(name, "-"); - // Timestamp lives at index 2 but we skip parsing it as it's not needed. - while (substr != nullptr && index < 2) { - if (index) { + while (substr != nullptr && index < 3) { + if (index == 0) { uid = atoi(substr); - } else { - configName = substr; + } else if (index == 1) { + configID = StrToInt64(substr); + } else if (index == 2) { + timestamp = atoi(substr); } index++; + substr = strtok(nullptr, "-"); } - if (index < 2) continue; + if (index < 3) continue; - string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name); - VLOG("full file %s", file_name.c_str()); + string file_name = StringPrintf("%s/%d-%lld-%lld", STATS_SERVICE_DIR, uid, + (long long)configID, (long long)timestamp); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); if (fd != -1) { string content; if (android::base::ReadFdToString(fd, &content)) { StatsdConfig config; if (config.ParseFromString(content)) { - configsMap[ConfigKey(uid, StrToInt64(configName))] = config; - VLOG("map key uid=%d|name=%s", uid, name); + configsMap[ConfigKey(uid, configID)] = config; + VLOG("map key uid=%d|configID=%lld", uid, (long long)configID); } } close(fd); diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc index c260ae1aa6c8..920273b5dfaa 100644 --- a/cmds/statsd/statsd.rc +++ b/cmds/statsd/statsd.rc @@ -16,6 +16,7 @@ service statsd /system/bin/statsd class main user statsd group statsd log + writepid /dev/cpuset/system-background/tasks on post-fs-data # Create directory for statsd diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index cc02f34519cd..62bdba406de2 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -91,7 +91,7 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) { { InSequence s; - manager->Startup(); + manager->StartupForTest(); // Add another one EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, StringToId("zzz")), diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index fe0f59d30c81..f90ca409e84c 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -81,8 +81,8 @@ StatsdConfig buildGoodConfig() { metric->set_id(3); metric->set_what(StringToId("SCREEN_IS_ON")); metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions()->add_child()->set_field(1); + metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions_in_what()->add_child()->set_field(1); config.add_no_report_metric(3); @@ -132,8 +132,8 @@ StatsdConfig buildAlertWithUnknownMetric() { metric->set_id(3); metric->set_what(StringToId("SCREEN_IS_ON")); metric->set_bucket(ONE_MINUTE); - metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); - metric->mutable_dimensions()->add_child()->set_field(1); + metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions_in_what()->add_child()->set_field(1); auto alert = config.add_alert(); alert->set_id(3); @@ -217,7 +217,7 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { metric->set_what(StringToId("BATTERY_LOW")); metric->set_bucket(ONE_MINUTE); // This case is interesting. We want to dimension across two atoms. - metric->mutable_dimensions()->add_child()->set_field(1); + metric->mutable_dimensions_in_what()->add_child()->set_field(1); auto alert = config.add_alert(); alert->set_id(103); diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index 39c95496055a..a56db28e64b3 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -44,7 +44,7 @@ StatsdConfig CreateStatsdConfig() { auto countMetric = config.add_count_metric(); countMetric->set_id(123456); countMetric->set_what(wakelockAcquireMatcher.id()); - *countMetric->mutable_dimensions() = + *countMetric->mutable_dimensions_in_what() = CreateAttributionUidAndTagDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); countMetric->set_bucket(ONE_MINUTE); @@ -155,7 +155,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { auto data = countMetrics.data(0); ValidateAttributionUidAndTagDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111, "App1"); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111, + "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 2); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -166,7 +167,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { data = countMetrics.data(1); ValidateAttributionUidAndTagDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1"); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); @@ -177,7 +179,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { data = countMetrics.data(2); ValidateAttributionUidAndTagDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule3"); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, + "GMSCoreModule3"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); @@ -185,7 +188,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { data = countMetrics.data(3); ValidateAttributionUidAndTagDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2"); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444, + "GMSCoreModule2"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp index 10a6c363eab3..e56a6c57848f 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp @@ -51,16 +51,16 @@ StatsdConfig CreateStatsdConfigForPushedEvent() { fieldMatcher->add_child()->set_field(3); // type (enum) fieldMatcher->add_child()->set_field(4); // activity_name(str) fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) - *gaugeMetric->mutable_dimensions() = + *gaugeMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ }); gaugeMetric->set_bucket(ONE_MINUTE); auto links = gaugeMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); - auto dimensionWhat = links->mutable_dimensions_in_what(); + auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::APP_START_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_dimensions_in_condition(); + auto dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionCondition->add_child()->set_field(1); // uid field. return config; @@ -148,10 +148,10 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(gaugeMetrics.data_size(), 2); auto data = gaugeMetrics.data(0); - EXPECT_EQ(data.dimension().field(), android::util::APP_START_CHANGED); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1 /* uid field */); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid1); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid1); EXPECT_EQ(data.bucket_info_size(), 3); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); @@ -172,10 +172,11 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(data.bucket_info(2).atom().app_start_changed().activity_start_msec(), 105L); data = gaugeMetrics.data(1); - EXPECT_EQ(data.dimension().field(), android::util::APP_START_CHANGED); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1 /* uid field */); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid2); + + EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1 /* uid field */); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid2); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index cdc44675a54d..eda16a2ffbfd 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -46,7 +46,7 @@ StatsdConfig CreateStatsdConfig() { auto isSyncingPredicate = CreateIsSyncingPredicate(); *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() = CreateDimensions( - android::util::SYNC_STATE_CHANGED, {1 /* uid field */}); + android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/}); auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = @@ -68,27 +68,27 @@ StatsdConfig CreateStatsdConfig() { countMetric->set_what(appCrashMatcher.id()); countMetric->set_condition(combinationPredicate->id()); // The metric is dimensioning by uid only. - *countMetric->mutable_dimensions() = + *countMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); countMetric->set_bucket(ONE_MINUTE); // Links between crash atom and condition of app is in syncing. auto links = countMetric->add_links(); links->set_condition(isSyncingPredicate.id()); - auto dimensionWhat = links->mutable_dimensions_in_what(); + auto dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - auto dimensionCondition = links->mutable_dimensions_in_condition(); + auto dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED); dimensionCondition->add_child()->set_field(1); // uid field. // Links between crash atom and condition of app is in background. links = countMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); - dimensionWhat = links->mutable_dimensions_in_what(); + dimensionWhat = links->mutable_fields_in_what(); dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); dimensionWhat->add_child()->set_field(1); // uid field. - dimensionCondition = links->mutable_dimensions_in_condition(); + dimensionCondition = links->mutable_fields_in_condition(); dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); dimensionCondition->add_child()->set_field(1); // uid field. return config; @@ -199,12 +199,11 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); auto data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. - EXPECT_EQ(data.dimension().field(), - android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); // Uid field. - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); reports.Clear(); processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); @@ -216,12 +215,12 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); data = reports.reports(0).metrics(0).count_metrics().data(0); // Validate dimension value. - EXPECT_EQ(data.dimension().field(), + EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); // Uid field. - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); - EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid); } #else diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index fcdaafce6627..e656b98c66ad 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -51,7 +51,7 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) durationMetric->set_condition(screenIsOffPredicate.id()); durationMetric->set_aggregation_type(aggregationType); // The metric is dimensioning by first attribution node and only by uid. - *durationMetric->mutable_dimensions() = + *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); durationMetric->set_bucket(ONE_MINUTE); @@ -125,7 +125,8 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { auto data = reports.reports(0).metrics(0).duration_metrics().data(0); // Validate dimension value. ValidateAttributionUidDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111); + data.dimensions_in_what(), + android::util::WAKELOCK_STATE_CHANGED, 111); // Validate bucket info. EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); data = reports.reports(0).metrics(0).duration_metrics().data(0); @@ -143,7 +144,7 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { data = reports.reports(0).metrics(0).duration_metrics().data(0); // Validate dimension value. ValidateAttributionUidDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); // Two output buckets. // The wakelock holding interval in the 1st bucket starts from the screen off event and to // the end of the 1st bucket. @@ -172,7 +173,7 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6); data = reports.reports(0).metrics(0).duration_metrics().data(0); ValidateAttributionUidDimension( - data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 111); + data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111); // The last wakelock holding spans 4 buckets. EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100); EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs); diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index c39151313c55..897328d4635e 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -149,8 +149,8 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); MetricConditionLink* link = metric.add_links(); link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); - *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); - *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); + *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); + *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); LogEvent event1(tagId, bucketStartTimeNs + 1); event1.write("111"); // uid diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 7171de939c62..34cde607988e 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -98,8 +98,8 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); MetricConditionLink* link = metric.add_links(); link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); - *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); - *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); + *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); + *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); LogEvent event1(tagId, bucketStartTimeNs + 1); EXPECT_TRUE(event1.write("111")); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 7eb93b9d5fcd..1fc33ded13c1 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -138,7 +138,7 @@ template <typename T> void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) { std::map<HashableDimensionKey, int> dimensionIndexMap; for (int i = 0; i < metricData.data_size(); ++i) { - dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimension(), i)); + dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i)); } for (const auto& itr : dimensionIndexMap) { *sortedMetricData->add_data() = metricData.data(itr.second); diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk index a4c080063284..32a85b136023 100644 --- a/cmds/statsd/tools/dogfood/Android.mk +++ b/cmds/statsd/tools/dogfood/Android.mk @@ -19,13 +19,10 @@ include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := StatsdDogfood LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_SRC_FILES += ../../src/stats_log.proto \ - ../../src/atoms.proto \ - ../../src/statsd_config.proto -LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite +LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \ + statsdprotolite LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_PRIVILEGED_MODULE := true diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java index 93dba71716a5..33c8abf03462 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java @@ -97,9 +97,14 @@ public class DisplayProtoUtils { = log.getDurationMetrics(); sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { - sb.append("dimension: "); - displayDimension(sb, duration.getDimension()); + sb.append("dimension_in_what: "); + displayDimension(sb, duration.getDimensionsInWhat()); sb.append("\n"); + if (duration.hasDimensionsInCondition()) { + sb.append("dimension_in_condition: "); + displayDimension(sb, duration.getDimensionsInCondition()); + sb.append("\n"); + } for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") @@ -124,9 +129,14 @@ public class DisplayProtoUtils { = log.getCountMetrics(); sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { - sb.append("dimension: "); - displayDimension(sb, count.getDimension()); + sb.append("dimension_in_what: "); + displayDimension(sb, count.getDimensionsInWhat()); sb.append("\n"); + if (count.hasDimensionsInCondition()) { + sb.append("dimension_in_condition: "); + displayDimension(sb, count.getDimensionsInCondition()); + sb.append("\n"); + } for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk index 0a0fd6647fbb..f5722c2c3d19 100644 --- a/cmds/statsd/tools/loadtest/Android.mk +++ b/cmds/statsd/tools/loadtest/Android.mk @@ -21,6 +21,7 @@ LOCAL_PACKAGE_NAME := StatsdLoadtest LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += ../../src/stats_log.proto \ ../../src/atoms.proto \ + ../../src/perfetto/perfetto_config.proto \ ../../src/statsd_config.proto LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java index 862ebe143e86..75489ade6432 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java @@ -108,9 +108,14 @@ public class DisplayProtoUtils { = log.getDurationMetrics(); sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { - sb.append("dimension: "); - displayDimension(sb, duration.getDimension()); + sb.append("dimension_in_what: "); + displayDimension(sb, duration.getDimensionsInWhat()); sb.append("\n"); + if (duration.hasDimensionsInCondition()) { + sb.append("dimension_in_condition: "); + displayDimension(sb, duration.getDimensionsInCondition()); + sb.append("\n"); + } for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") @@ -135,9 +140,14 @@ public class DisplayProtoUtils { = log.getCountMetrics(); sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { - sb.append("dimension: "); - displayDimension(sb, count.getDimension()); + sb.append("dimension_in_what: "); + displayDimension(sb, count.getDimensionsInWhat()); sb.append("\n"); + if (count.hasDimensionsInCondition()) { + sb.append("dimension_in_condition: "); + displayDimension(sb, count.getDimensionsInCondition()); + sb.append("\n"); + } for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 97dcb90bbb99..0a4541ba4777 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -363,6 +363,11 @@ public abstract class AccessibilityService extends Service { */ public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; + /** + * Action to take a screenshot + */ + public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; + private static final String LOG_TAG = "AccessibilityService"; /** diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index cfe557252fee..0a5b848e6220 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,6 +16,7 @@ package android.app; +import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -98,6 +99,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.RemoteAnimationDefinition; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; @@ -857,6 +859,7 @@ public class Activity extends ContextThemeWrapper private boolean mHasCurrentPermissionsRequest; private boolean mAutoFillResetNeeded; + private boolean mAutoFillIgnoreFirstResumePause; /** The last autofill id that was returned from {@link #getNextAutofillId()} */ private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID; @@ -1253,10 +1256,7 @@ public class Activity extends ContextThemeWrapper getApplication().dispatchActivityStarted(this); if (mAutoFillResetNeeded) { - AutofillManager afm = getAutofillManager(); - if (afm != null) { - afm.onVisibleForAutofill(); - } + getAutofillManager().onVisibleForAutofill(); } } @@ -1320,6 +1320,20 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); getApplication().dispatchActivityResumed(this); mActivityTransitionState.onResume(this, isTopOfTask()); + if (mAutoFillResetNeeded) { + if (!mAutoFillIgnoreFirstResumePause) { + View focus = getCurrentFocus(); + if (focus != null && focus.canNotifyAutofillEnterExitEvent()) { + // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest# + // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial + // window visibility after recreation is INVISIBLE in onResume() and next frame + // ViewRootImpl.performTraversals() changes window visibility to VISIBLE. + // So we cannot call View.notifyEnterOrExited() which will do nothing + // when View.isVisibleToUser() is false. + getAutofillManager().notifyViewEntered(focus); + } + } + } mCalled = true; } @@ -1681,6 +1695,19 @@ public class Activity extends ContextThemeWrapper protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); getApplication().dispatchActivityPaused(this); + if (mAutoFillResetNeeded) { + if (!mAutoFillIgnoreFirstResumePause) { + if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this); + View focus = getCurrentFocus(); + if (focus != null && focus.canNotifyAutofillEnterExitEvent()) { + getAutofillManager().notifyViewExited(focus); + } + } else { + // reset after first pause() + if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this); + mAutoFillIgnoreFirstResumePause = false; + } + } mCalled = true; } @@ -1871,6 +1898,10 @@ public class Activity extends ContextThemeWrapper mTranslucentCallback = null; mCalled = true; + if (mAutoFillResetNeeded) { + getAutofillManager().onInvisibleForAutofill(); + } + if (isFinishing()) { if (mAutoFillResetNeeded) { getAutofillManager().onActivityFinished(); @@ -6266,7 +6297,7 @@ public class Activity extends ContextThemeWrapper mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); - final AutofillManager afm = getAutofillManager(); + final AutofillManager afm = mAutofillManager; if (afm != null) { afm.dump(prefix, writer); } else { @@ -7119,13 +7150,23 @@ public class Activity extends ContextThemeWrapper } } - final void performResume() { + final void performResume(boolean followedByPause) { performRestart(true /* start */); mFragments.execPendingActions(); mLastNonConfigurationInstances = null; + if (mAutoFillResetNeeded) { + // When Activity is destroyed in paused state, and relaunch activity, there will be + // extra onResume and onPause event, ignore the first onResume and onPause. + // see ActivityThread.handleRelaunchActivity() + mAutoFillIgnoreFirstResumePause = followedByPause; + if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) { + Slog.v(TAG, "autofill will ignore first pause when relaunching " + this); + } + } + mCalled = false; // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); @@ -7310,7 +7351,7 @@ public class Activity extends ContextThemeWrapper } } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) { Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null; - getAutofillManager().onAuthenticationResult(requestCode, resultData); + getAutofillManager().onAuthenticationResult(requestCode, resultData, getCurrentFocus()); } else { Fragment frag = mFragments.findFragmentByWho(who); if (frag != null) { @@ -7584,6 +7625,12 @@ public class Activity extends ContextThemeWrapper return !mStopped; } + /** @hide */ + @Override + public boolean isDisablingEnterExitEventForAutofill() { + return mAutoFillIgnoreFirstResumePause || !mResumed; + } + /** * If set to true, this indicates to the system that it should never take a * screenshot of the activity to be used as a representation while it is not in a started state. @@ -7658,6 +7705,22 @@ public class Activity extends ContextThemeWrapper } } + /** + * Registers remote animations per transition type for this activity. + * + * @param definition The remote animation definition that defines which transition whould run + * which remote animation. + * @hide + */ + @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) + public void registerRemoteAnimations(RemoteAnimationDefinition definition) { + try { + ActivityManager.getService().registerRemoteAnimations(mToken, definition); + } catch (RemoteException e) { + Log.e(TAG, "Failed to call registerRemoteAnimations", e); + } + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 972ffcbf527f..ccb54f93ba33 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -324,4 +324,19 @@ public abstract class ActivityManagerInternal { * Returns if more users can be started without stopping currently running users. */ public abstract boolean canStartMoreUsers(); + + /** + * Sets the user switcher message for switching from {@link android.os.UserHandle#SYSTEM}. + */ + public abstract void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage); + + /** + * Sets the user switcher message for switching to {@link android.os.UserHandle#SYSTEM}. + */ + public abstract void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage); + + /** + * Returns maximum number of users that can run simultaneously. + */ + public abstract int getMaxRunningUsers(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 70c383b29c63..934b0f3cd4e4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -166,6 +166,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.InetAddress; import java.text.DateFormat; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -220,6 +221,9 @@ public final class ActivityThread extends ClientTransactionHandler { // Whether to invoke an activity callback after delivering new configuration. private static final boolean REPORT_TO_ACTIVITY = true; + // Maximum number of recent tokens to maintain for debugging purposes + private static final int MAX_RECENT_TOKENS = 10; + /** * Denotes an invalid sequence number corresponding to a process state change. */ @@ -252,6 +256,8 @@ public final class ActivityThread extends ClientTransactionHandler { final H mH = new H(); final Executor mExecutor = new HandlerExecutor(mH); final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); + final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>(); + // List of new activities (via ActivityRecord.nextIdle) that should // be reported when next we idle. ActivityClientRecord mNewActivities = null; @@ -2168,6 +2174,18 @@ public final class ActivityThread extends ClientTransactionHandler { pw.println(String.format(format, objs)); } + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "mActivities:"); + + for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) { + pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:" + + entry.getValue().toString() + "]"); + } + + pw.println(prefix + "mRecentTokens:" + mRecentTokens); + } + public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, int pid, String processName, @@ -2852,6 +2870,11 @@ public final class ActivityThread extends ClientTransactionHandler { r.setState(ON_CREATE); mActivities.put(r.token, r); + mRecentTokens.push(r.token.hashCode()); + + if (mRecentTokens.size() > MAX_RECENT_TOKENS) { + mRecentTokens.removeLast(); + } } catch (SuperNotCalledException e) { throw e; @@ -3073,7 +3096,7 @@ public final class ActivityThread extends ClientTransactionHandler { checkAndBlockForNetworkAccess(); deliverNewIntents(r, intents); if (resumed) { - r.activity.performResume(); + r.activity.performResume(false); r.activity.mTemporaryPause = false; } @@ -3695,7 +3718,7 @@ public final class ActivityThread extends ClientTransactionHandler { deliverResults(r, r.pendingResults); r.pendingResults = null; } - r.activity.performResume(); + r.activity.performResume(r.startsNotResumed); synchronized (mResourcesManager) { // If there is a pending local relaunch that was requested when the activity was @@ -4414,7 +4437,7 @@ public final class ActivityThread extends ClientTransactionHandler { checkAndBlockForNetworkAccess(); deliverResults(r, results); if (resumed) { - r.activity.performResume(); + r.activity.performResume(false); r.activity.mTemporaryPause = false; } } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 9f1e98399dce..ac6cba138eed 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -34,6 +34,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import dalvik.system.CloseGuard; @@ -58,6 +59,8 @@ public class ActivityView extends ViewGroup { private StateCallback mActivityViewCallback; private IInputForwarder mInputForwarder; + // Temp container to store view coordinates on screen. + private final int[] mLocationOnScreen = new int[2]; private final CloseGuard mGuard = CloseGuard.get(); private boolean mOpened; // Protected by mGuard. @@ -198,11 +201,30 @@ public class ActivityView extends ViewGroup { performRelease(); } + /** + * Triggers an update of {@link ActivityView}'s location on screen to properly set touch exclude + * regions and avoid focus switches by touches on this view. + */ + public void onLocationChanged() { + updateLocation(); + } + @Override public void onLayout(boolean changed, int l, int t, int r, int b) { mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */); } + /** Send current location and size to the WM to set tap exclude region for this view. */ + private void updateLocation() { + try { + getLocationOnScreen(mLocationOnScreen); + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), + mLocationOnScreen[0], mLocationOnScreen[1], getWidth(), getHeight()); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + @Override public boolean onTouchEvent(MotionEvent event) { return injectInputEvent(event) || super.onTouchEvent(event); @@ -241,6 +263,7 @@ public class ActivityView extends ViewGroup { } else { mVirtualDisplay.setSurface(surfaceHolder.getSurface()); } + updateLocation(); } @Override @@ -248,6 +271,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } + updateLocation(); } @Override @@ -257,6 +281,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.setSurface(null); } + cleanTapExcludeRegion(); } } @@ -290,6 +315,7 @@ public class ActivityView extends ViewGroup { if (mInputForwarder != null) { mInputForwarder = null; } + cleanTapExcludeRegion(); final boolean displayReleased; if (mVirtualDisplay != null) { @@ -313,6 +339,17 @@ public class ActivityView extends ViewGroup { mOpened = false; } + /** Report to server that tap exclude region on hosting display should be cleared. */ + private void cleanTapExcludeRegion() { + // Update tap exclude region with an empty rect to clean the state on server. + try { + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), + 0 /* left */, 0 /* top */, 0 /* width */, 0 /* height */); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + /** Get density of the hosting display. */ private int getBaseDisplayDensity() { final WindowManager wm = mContext.getSystemService(WindowManager.class); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 57f9f67b4f9c..7ca680239eef 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1326,6 +1326,25 @@ public class AppOpsManager { } /** + * Retrieve the human readable mode. + * @hide + */ + public static String modeToString(int mode) { + switch (mode) { + case MODE_ALLOWED: + return "allow"; + case MODE_IGNORED: + return "ignore"; + case MODE_ERRORED: + return "deny"; + case MODE_DEFAULT: + return "default"; + default: + return "mode=" + mode; + } + } + + /** * Retrieve whether the op allows itself to be reset. * @hide */ diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 45c0e0cdfb25..0f66652af76c 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -24,6 +24,7 @@ import android.os.IBinder; import com.android.internal.content.ReferrerIntent; +import java.io.PrintWriter; import java.util.List; /** @@ -121,4 +122,11 @@ public abstract class ClientTransactionHandler { * provided token. */ public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token); + + /** + * Debugging output. + * @param pw {@link PrintWriter} to write logs to. + * @param prefix Prefix to prepend to output. + */ + public abstract void dump(PrintWriter pw, String prefix); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 165343058a2a..4914ffaf8912 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -872,13 +872,19 @@ class ContextImpl extends Context { // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should - // be launched in. - if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 - && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { + // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We + // maintain this for backwards compatibility. + final int targetSdkVersion = getApplicationInfo().targetSdkVersion; + + if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 + && (targetSdkVersion < Build.VERSION_CODES.N + || targetSdkVersion >= Build.VERSION_CODES.P) + && (options == null + || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " - + " context requires the FLAG_ACTIVITY_NEW_TASK flag." - + " Is this really what you want?"); + + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 696899f73b96..04ee77d764aa 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -65,6 +65,7 @@ import android.os.PersistableBundle; import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; +import android.view.RemoteAnimationDefinition; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; @@ -672,4 +673,9 @@ interface IActivityManager { * user unlock progress. */ boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener); + + /** + * Registers remote animations for a specific activity. + */ + void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index b469de56d857..c5a58f2eef30 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -458,7 +459,8 @@ public class Instrumentation { * * @see Context#startActivity(Intent, Bundle) */ - public Activity startActivitySync(Intent intent, @Nullable Bundle options) { + @NonNull + public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) { validateNotAppThread(); synchronized (mSync) { diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java index 02a01242c3ba..b7100e6f4d87 100644 --- a/core/java/android/app/RemoteInput.java +++ b/core/java/android/app/RemoteInput.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; + import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -73,6 +74,15 @@ public final class RemoteInput implements Parcelable { private static final String EXTRA_DATA_TYPE_RESULTS_DATA = "android.remoteinput.dataTypeResultsData"; + /** Extra added to a clip data intent object identifying the source of the results. */ + private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource"; + + /** The user manually entered the data. */ + public static final int SOURCE_FREE_FORM_INPUT = 0; + + /** The user selected one of the choices from {@link #getChoices}. */ + public static final int SOURCE_CHOICE = 1; + // Flags bitwise-ored to mFlags private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1; @@ -416,6 +426,48 @@ public final class RemoteInput implements Parcelable { intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent)); } + /** + * Set the source of the RemoteInput results. This method should only be called by remote + * input collection services (e.g. + * {@link android.service.notification.NotificationListenerService}) + * when sending results to a pending intent. + * + * @see #SOURCE_FREE_FORM_INPUT + * @see #SOURCE_CHOICE + * + * @param intent The intent to add remote input source to. The {@link ClipData} + * field of the intent will be modified to contain the source. + * field of the intent will be modified to contain the source. + * @param source The source of the results. + */ + public static void setResultsSource(Intent intent, int source) { + Intent clipDataIntent = getClipDataIntentFromIntent(intent); + if (clipDataIntent == null) { + clipDataIntent = new Intent(); // First time we've added a result. + } + clipDataIntent.putExtra(EXTRA_RESULTS_SOURCE, source); + intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent)); + } + + /** + * Get the source of the RemoteInput results. + * + * @see #SOURCE_FREE_FORM_INPUT + * @see #SOURCE_CHOICE + * + * @param intent The intent object that fired in response to an action or content intent + * which also had one or more remote input requested. + * @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will + * be returned. + */ + public static int getResultsSource(Intent intent) { + Intent clipDataIntent = getClipDataIntentFromIntent(intent); + if (clipDataIntent == null) { + return SOURCE_FREE_FORM_INPUT; + } + return clipDataIntent.getExtras().getInt(EXTRA_RESULTS_SOURCE, SOURCE_FREE_FORM_INPUT); + } + private static String getExtraResultsKeyForData(String mimeType) { return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType; } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index b5662cbf8300..fb8d1017e205 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -38,12 +38,12 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.IRestrictionsManager; import android.content.RestrictionsManager; +import android.content.pm.CrossProfileApps; +import android.content.pm.ICrossProfileApps; import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; -import android.content.pm.crossprofile.CrossProfileApps; -import android.content.pm.crossprofile.ICrossProfileApps; import android.content.res.Resources; import android.hardware.ConsumerIrManager; import android.hardware.ISerialManager; diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index aa05b7630c9d..ffb3affb03b3 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -339,7 +339,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { /** * Broadcast action: notify the device owner that a user or profile has been removed. * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of - * the new user. + * the user. * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -347,6 +347,36 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String ACTION_USER_REMOVED = "android.app.action.USER_REMOVED"; /** + * Broadcast action: notify the device owner that a user or profile has been started. + * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of + * the user. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(explicitOnly = true) + public static final String ACTION_USER_STARTED = "android.app.action.USER_STARTED"; + + /** + * Broadcast action: notify the device owner that a user or profile has been stopped. + * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of + * the user. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(explicitOnly = true) + public static final String ACTION_USER_STOPPED = "android.app.action.USER_STOPPED"; + + /** + * Broadcast action: notify the device owner that a user or profile has been switched to. + * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of + * the user. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(explicitOnly = true) + public static final String ACTION_USER_SWITCHED = "android.app.action.USER_SWITCHED"; + + /** * A string containing the SHA-256 hash of the bugreport file. * * @see #ACTION_BUGREPORT_SHARE @@ -467,6 +497,31 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE = "android.app.extra.TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE"; + /** + * Name under which a device administration component indicates whether it supports transfer of + * ownership. This meta-data is of type <code>boolean</code>. A value of <code>true</code> + * allows this administrator to be used as a target administrator for a transfer. If the value + * is <code>false</code>, ownership cannot be transferred to this administrator. The default + * value is <code>false</code>. + * <p>This metadata is used to avoid ownership transfer migration to an administrator with a + * version which does not yet support it. + * <p>Usage: + * <pre> + * <receiver name="..." android:permission="android.permission.BIND_DEVICE_ADMIN"> + * <meta-data + * android:name="android.app.device_admin" + * android:resource="@xml/..." /> + * <meta-data + * android:name="android.app.support_transfer_ownership" + * android:value="true" /> + * </receiver> + * </pre> + * + * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle) + */ + public static final String SUPPORT_TRANSFER_OWNERSHIP_META_DATA = + "android.app.support_transfer_ownership"; + private DevicePolicyManager mManager; private ComponentName mWho; @@ -889,6 +944,42 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } /** + * Called when a user or profile is started. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param startedUser The {@link UserHandle} of the user that has just been started. + */ + public void onUserStarted(Context context, Intent intent, UserHandle startedUser) { + } + + /** + * Called when a user or profile is stopped. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param stoppedUser The {@link UserHandle} of the user that has just been stopped. + */ + public void onUserStopped(Context context, Intent intent, UserHandle stoppedUser) { + } + + /** + * Called when a user or profile is switched to. + * + * <p>This callback is only applicable to device owners. + * + * @param context The running context as per {@link #onReceive}. + * @param intent The received intent as per {@link #onReceive}. + * @param switchedUser The {@link UserHandle} of the user that has just been switched to. + */ + public void onUserSwitched(Context context, Intent intent, UserHandle switchedUser) { + } + + /** * Called on the newly assigned owner (either device owner or profile owner) when the ownership * transfer has completed successfully. * @@ -964,6 +1055,12 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER)); } else if (ACTION_USER_REMOVED.equals(action)) { onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER)); + } else if (ACTION_USER_STARTED.equals(action)) { + onUserStarted(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER)); + } else if (ACTION_USER_STOPPED.equals(action)) { + onUserStopped(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER)); + } else if (ACTION_USER_SWITCHED.equals(action)) { + onUserSwitched(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER)); } else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) { PersistableBundle bundle = intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0455949a7cff..7fccda8c04e3 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1157,9 +1157,17 @@ public class DevicePolicyManager { public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture"; /** + * Constant to indicate the feature of mandatory backups. Used as argument to + * {@link #createAdminSupportIntent(String)}. + * @see #setMandatoryBackupTransport(ComponentName, ComponentName) + */ + public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups"; + + /** * A String indicating a specific restricted feature. Can be a user restriction from the * {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values - * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}. + * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or + * {@link #POLICY_MANDATORY_BACKUPS}. * @see #createAdminSupportIntent(String) * @hide */ @@ -6806,7 +6814,8 @@ public class DevicePolicyManager { * @param restriction Indicates for which feature the dialog should be displayed. Can be a * user restriction from {@link UserManager}, e.g. * {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants - * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}. + * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or + * {@link #POLICY_MANDATORY_BACKUPS}. * @return Intent An intent to be used to start the dialog-activity if the restriction is * set by an admin, or null if the restriction does not exist or no admin set it. */ @@ -9096,6 +9105,11 @@ public class DevicePolicyManager { * will be received in the * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)} callback. * + * <p>The incoming target administrator must have the + * {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag + * included in its corresponding <code>receiver</code> component with a value of {@code true}. + * Otherwise an {@link IllegalArgumentException} will be thrown. + * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param target which {@link DeviceAdminReceiver} we want the new administrator to be * @param bundle data to be sent to the new administrator @@ -9112,4 +9126,135 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Called by a device owner to specify the user session start message. This may be displayed + * during a user switch. + * <p> + * The message should be limited to a short statement or it may be truncated. + * <p> + * If the message needs to be localized, it is the responsibility of the + * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast + * and set a new version of this message accordingly. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param startUserSessionMessage message for starting user session, or {@code null} to use + * system default message. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public void setStartUserSessionMessage( + @NonNull ComponentName admin, @Nullable CharSequence startUserSessionMessage) { + throwIfParentInstance("setStartUserSessionMessage"); + try { + mService.setStartUserSessionMessage(admin, startUserSessionMessage); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Called by a device owner to specify the user session end message. This may be displayed + * during a user switch. + * <p> + * The message should be limited to a short statement or it may be truncated. + * <p> + * If the message needs to be localized, it is the responsibility of the + * {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast + * and set a new version of this message accordingly. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param endUserSessionMessage message for ending user session, or {@code null} to use system + * default message. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public void setEndUserSessionMessage( + @NonNull ComponentName admin, @Nullable CharSequence endUserSessionMessage) { + throwIfParentInstance("setEndUserSessionMessage"); + try { + mService.setEndUserSessionMessage(admin, endUserSessionMessage); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns the user session start message. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public CharSequence getStartUserSessionMessage(@NonNull ComponentName admin) { + throwIfParentInstance("getStartUserSessionMessage"); + try { + return mService.getStartUserSessionMessage(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns the user session end message. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public CharSequence getEndUserSessionMessage(@NonNull ComponentName admin) { + throwIfParentInstance("getEndUserSessionMessage"); + try { + return mService.getEndUserSessionMessage(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Allows/disallows printing. + * + * Called by a device owner or a profile owner. + * Device owner changes policy for all users. Profile owner can override it if present. + * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled whether printing should be allowed or not. + * @throws SecurityException if {@code admin} is neither device, nor profile owner. + */ + public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) { + try { + mService.setPrintingEnabled(admin, enabled); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns whether printing is enabled for this user. + * + * Always {@code false} if {@code FEATURE_PRINTING} is absent. + * Otherwise, {@code true} by default. + * + * @return {@code true} iff printing is enabled. + */ + public boolean isPrintingEnabled() { + try { + return mService.isPrintingEnabled(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Returns error message to be displayed when printing is disabled. + * + * Used only by PrintService. + * @return Localized error message. + * @hide + */ + @SystemApi + public CharSequence getPrintingDisabledReason() { + try { + return mService.getPrintingDisabledReason(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 9cdd1f836d45..d2a2be7bbcb5 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -391,4 +391,13 @@ interface IDevicePolicyManager { List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction); void transferOwnership(in ComponentName admin, in ComponentName target, in PersistableBundle bundle); + + void setStartUserSessionMessage(in ComponentName admin, in CharSequence startUserSessionMessage); + void setEndUserSessionMessage(in ComponentName admin, in CharSequence endUserSessionMessage); + CharSequence getStartUserSessionMessage(in ComponentName admin); + CharSequence getEndUserSessionMessage(in ComponentName admin); + + void setPrintingEnabled(in ComponentName admin, boolean enabled); + boolean isPrintingEnabled(); + CharSequence getPrintingDisabledReason(); } diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 7c40b4eaf375..cba9dcc3c4cc 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -253,6 +253,11 @@ public class JobInfo implements Parcelable { /** * @hide */ + public static final int FLAG_IS_PREFETCH = 1 << 2; + + /** + * @hide + */ public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0; /** @@ -1364,6 +1369,28 @@ public class JobInfo implements Parcelable { } /** + * Setting this to true indicates that this job is designed to prefetch + * content that will make a material improvement to the experience of + * the specific user of this device. For example, fetching top headlines + * of interest to the current user. + * <p> + * The system may use this signal to relax the network constraints you + * originally requested, such as allowing a + * {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over a metered + * network when there is a surplus of metered data available. The system + * may also use this signal in combination with end user usage patterns + * to ensure data is prefetched before the user launches your app. + */ + public Builder setIsPrefetch(boolean isPrefetch) { + if (isPrefetch) { + mFlags |= FLAG_IS_PREFETCH; + } else { + mFlags &= (~FLAG_IS_PREFETCH); + } + return this; + } + + /** * Set whether or not to persist this job across device reboots. * * @param isPersisted True to indicate that the job will be written to diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java index 0fdc7c56fd01..9a50a009ce34 100644 --- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java +++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java @@ -17,7 +17,9 @@ package android.app.servertransaction; import android.annotation.IntDef; +import android.os.Parcel; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -26,6 +28,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ public abstract class ActivityLifecycleItem extends ClientTransactionItem { + private String mDescription; @IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = { UNDEFINED, @@ -53,4 +56,39 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem { /** A final lifecycle state that an activity should reach. */ @LifecycleState public abstract int getTargetState(); + + + protected ActivityLifecycleItem() { + } + + protected ActivityLifecycleItem(Parcel in) { + mDescription = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mDescription); + } + + /** + * Sets a description that can be retrieved later for debugging purposes. + * @param description Description to set. + * @return The {@link ActivityLifecycleItem}. + */ + public ActivityLifecycleItem setDescription(String description) { + mDescription = description; + return this; + } + + /** + * Retrieves description if set through {@link #setDescription(String)}. + */ + public String getDescription() { + return mDescription; + } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "target state:" + getTargetState()); + pw.println(prefix + "description: " + mDescription); + } } diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 08ad2f055774..fc078798f6b9 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -237,4 +238,12 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { result = 31 * result + Objects.hashCode(mLifecycleStateRequest); return result; } + + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "mActivityToken:" + mActivityToken.hashCode()); + pw.println(prefix + "mLifecycleStateRequest:"); + if (mLifecycleStateRequest != null) { + mLifecycleStateRequest.dump(pw, prefix + " "); + } + } } diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index 83da5f33c62a..cbcf6c750fed 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -76,12 +76,14 @@ public class DestroyActivityItem extends ActivityLifecycleItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); dest.writeBoolean(mFinished); dest.writeInt(mConfigChanges); } /** Read from Parcel. */ private DestroyActivityItem(Parcel in) { + super(in); mFinished = in.readBoolean(); mConfigChanges = in.readInt(); } diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 880fef73c6f2..70a4755f99af 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -114,6 +114,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); dest.writeBoolean(mFinished); dest.writeBoolean(mUserLeaving); dest.writeInt(mConfigChanges); @@ -122,6 +123,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { /** Read from Parcel. */ private PauseActivityItem(Parcel in) { + super(in); mFinished = in.readBoolean(); mUserLeaving = in.readBoolean(); mConfigChanges = in.readInt(); diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index 9249c6e8ed54..ed90f2cb1013 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -113,6 +113,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); dest.writeInt(mProcState); dest.writeBoolean(mUpdateProcState); dest.writeBoolean(mIsForward); @@ -120,6 +121,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { /** Read from Parcel. */ private ResumeActivityItem(Parcel in) { + super(in); mProcState = in.readInt(); mUpdateProcState = in.readBoolean(); mIsForward = in.readBoolean(); diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index 5c5c3041344f..b814d1ae1392 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -83,12 +83,14 @@ public class StopActivityItem extends ActivityLifecycleItem { /** Write to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); dest.writeBoolean(mShowWindow); dest.writeInt(mConfigChanges); } /** Read from Parcel. */ private StopActivityItem(Parcel in) { + super(in); mShowWindow = in.readBoolean(); mConfigChanges = in.readInt(); } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 5b0ea6b1f9d4..78b393a831f9 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -33,6 +33,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.List; /** @@ -122,6 +124,21 @@ public class TransactionExecutor { final IBinder token = transaction.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); + // TODO(b/71506345): Remove once root cause is found. + if (r == null) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter pw = new PrintWriter(stringWriter); + final String prefix = " "; + + pw.println("Lifecycle transaction does not have valid ActivityClientRecord."); + pw.println("Transaction:"); + transaction.dump(pw, prefix); + pw.println("Executor:"); + dump(pw, prefix); + + Slog.wtf(TAG, stringWriter.toString()); + } + // Cycle to the state right before the final requested state. cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */); @@ -245,4 +262,9 @@ public class TransactionExecutor { private static void log(String message) { if (DEBUG_RESOLVER) Slog.d(TAG, message); } + + private void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "mTransactionHandler:"); + mTransactionHandler.dump(pw, prefix + " "); + } } diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl index 5f0e542f4b12..4461b16fe15c 100644 --- a/core/java/android/app/slice/ISliceManager.aidl +++ b/core/java/android/app/slice/ISliceManager.aidl @@ -29,4 +29,6 @@ interface ISliceManager { void unpinSlice(String pkg, in Uri uri); boolean hasSliceAccess(String pkg); SliceSpec[] getPinnedSpecs(in Uri uri, String pkg); + int checkSlicePermission(in Uri uri, String pkg, int pid, int uid); + void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); } diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index 27cd6e56dc0c..5bd3440d09f8 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -65,6 +65,7 @@ public final class Slice implements Parcelable { HINT_TOGGLE, HINT_HORIZONTAL, HINT_PARTIAL, + HINT_SEE_MORE }) @Retention(RetentionPolicy.SOURCE) public @interface SliceHint {} @@ -149,7 +150,18 @@ public final class Slice implements Parcelable { * Used to indicate the maximum integer value for a {@link #SUBTYPE_SLIDER}. */ public static final String HINT_MAX = "max"; - + /** + * A hint representing that this item should be used to indicate that there's more + * content associated with this slice. + */ + public static final String HINT_SEE_MORE = "see_more"; + /** + * A hint to tell the system that this slice cares about the return value of + * {@link SliceProvider#getBindingPackage} and should not cache the result + * for multiple apps. + * @hide + */ + public static final String HINT_CALLER_NEEDED = "caller_needed"; /** * Key to retrieve an extra added to an intent when a control is changed. */ diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 74864cb1a371..09c420c3e66a 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -53,12 +53,34 @@ public class SliceManager { private static final String TAG = "SliceManager"; + /** + * @hide + */ + public static final String ACTION_REQUEST_SLICE_PERMISSION = + "android.intent.action.REQUEST_SLICE_PERMISSION"; + private final ISliceManager mService; private final Context mContext; private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup = new ArrayMap<>(); /** + * Permission denied. + * @hide + */ + public static final int PERMISSION_DENIED = -1; + /** + * Permission granted. + * @hide + */ + public static final int PERMISSION_GRANTED = 0; + /** + * Permission just granted by the user, and should be granted uri permission as well. + * @hide + */ + public static final int PERMISSION_USER_GRANTED = 1; + + /** * @hide */ public SliceManager(Context context, Handler handler) throws ServiceNotFoundException { @@ -284,7 +306,7 @@ public class SliceManager { extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri); extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, new ArrayList<>(supportedSpecs)); - final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE, + final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE, null, extras); Bundle.setDefusable(res, true); if (res == null) { @@ -342,7 +364,7 @@ public class SliceManager { extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, new ArrayList<>(supportedSpecs)); - final Bundle res = provider.call(resolver.getPackageName(), + final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_MAP_INTENT, null, extras); if (res == null) { return null; @@ -358,6 +380,45 @@ public class SliceManager { } /** + * Does the permission check to see if a caller has access to a specific slice. + * @hide + */ + public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) { + try { + if (pkg == null) { + throw new SecurityException("No pkg specified"); + } + int result = mService.checkSlicePermission(uri, pkg, pid, uid); + if (result == PERMISSION_DENIED) { + throw new SecurityException("User " + uid + " does not have slice permission for " + + uri + "."); + } + if (result == PERMISSION_USER_GRANTED) { + // We just had a user grant of this permission and need to grant this to the app + // permanently. + mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(), + Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Called by SystemUI to grant a slice permission after a dialog is shown. + * @hide + */ + public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) { + try { + mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Class that listens to changes in {@link Slice}s. */ public interface SliceCallback { diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index aa41f14d8cb5..c4316a0ba086 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -15,13 +15,19 @@ */ package android.app.slice; -import android.Manifest.permission; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.PendingIntent; +import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ProviderInfo; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; @@ -129,9 +135,41 @@ public abstract class SliceProvider extends ContentProvider { * @hide */ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants"; + /** + * @hide + */ + public static final String EXTRA_PKG = "pkg"; + /** + * @hide + */ + public static final String EXTRA_PROVIDER_PKG = "provider_pkg"; + /** + * @hide + */ + public static final String EXTRA_OVERRIDE_PKG = "override_pkg"; private static final boolean DEBUG = false; + private String mBindingPkg; + private SliceManager mSliceManager; + + /** + * Return the package name of the caller that initiated the binding request + * currently happening. The returned package will have been + * verified to belong to the calling UID. Returns {@code null} if not + * currently performing an {@link #onBindSlice(Uri, List)}. + * @hide + */ + public final @Nullable String getBindingPackage() { + return mBindingPkg; + } + + @Override + public void attachInfo(Context context, ProviderInfo info) { + super.attachInfo(context, info); + mSliceManager = context.getSystemService(SliceManager.class); + } + /** * Implemented to create a slice. Will be called on the main thread. * <p> @@ -261,54 +299,47 @@ public abstract class SliceProvider extends ContentProvider { @Override public Bundle call(String method, String arg, Bundle extras) { if (method.equals(METHOD_SLICE)) { - Uri uri = extras.getParcelable(EXTRA_BIND_URI); - if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) { - getContext().enforceUriPermission(uri, permission.BIND_SLICE, - permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(), - Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - "Slice binding requires the permission BIND_SLICE"); - } + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS); - Slice s = handleBindSlice(uri, supportedSpecs); + String callingPackage = getCallingPackage(); + if (extras.containsKey(EXTRA_OVERRIDE_PKG)) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can override calling pkg"); + } + callingPackage = extras.getString(EXTRA_OVERRIDE_PKG); + } + Slice s = handleBindSlice(uri, supportedSpecs, callingPackage); Bundle b = new Bundle(); b.putParcelable(EXTRA_SLICE, s); return b; } else if (method.equals(METHOD_MAP_INTENT)) { - getContext().enforceCallingPermission(permission.BIND_SLICE, - "Slice binding requires the permission BIND_SLICE"); Intent intent = extras.getParcelable(EXTRA_INTENT); if (intent == null) return null; Uri uri = onMapIntentToUri(intent); List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS); Bundle b = new Bundle(); if (uri != null) { - Slice s = handleBindSlice(uri, supportedSpecs); + Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage()); b.putParcelable(EXTRA_SLICE, s); } else { b.putParcelable(EXTRA_SLICE, null); } return b; } else if (method.equals(METHOD_PIN)) { - Uri uri = extras.getParcelable(EXTRA_BIND_URI); - if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) { - getContext().enforceUriPermission(uri, permission.BIND_SLICE, - permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(), - Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - "Slice binding requires the permission BIND_SLICE"); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can pin/unpin slices"); } handlePinSlice(uri); } else if (method.equals(METHOD_UNPIN)) { - Uri uri = extras.getParcelable(EXTRA_BIND_URI); - if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) { - getContext().enforceUriPermission(uri, permission.BIND_SLICE, - permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(), - Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - "Slice binding requires the permission BIND_SLICE"); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can pin/unpin slices"); } handleUnpinSlice(uri); } else if (method.equals(METHOD_GET_DESCENDANTS)) { - Uri uri = extras.getParcelable(EXTRA_BIND_URI); + Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI)); Bundle b = new Bundle(); b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS, new ArrayList<>(handleGetDescendants(uri))); @@ -370,14 +401,27 @@ public abstract class SliceProvider extends ContentProvider { } } - private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) { + private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs, + String callingPkg) { + // This can be removed once Slice#bindSlice is removed and everyone is using + // SliceManager#bindSlice. + String pkg = callingPkg != null ? callingPkg + : getContext().getPackageManager().getNameForUid(Binder.getCallingUid()); + if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) { + try { + mSliceManager.enforceSlicePermission(sliceUri, pkg, + Binder.getCallingPid(), Binder.getCallingUid()); + } catch (SecurityException e) { + return createPermissionSlice(getContext(), sliceUri, pkg); + } + } if (Looper.myLooper() == Looper.getMainLooper()) { - return onBindSliceStrict(sliceUri, supportedSpecs); + return onBindSliceStrict(sliceUri, supportedSpecs, pkg); } else { CountDownLatch latch = new CountDownLatch(1); Slice[] output = new Slice[1]; Handler.getMain().post(() -> { - output[0] = onBindSliceStrict(sliceUri, supportedSpecs); + output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg); latch.countDown(); }); try { @@ -389,15 +433,66 @@ public abstract class SliceProvider extends ContentProvider { } } - private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) { + /** + * @hide + */ + public static Slice createPermissionSlice(Context context, Uri sliceUri, + String callingPackage) { + return new Slice.Builder(sliceUri) + .addAction(createPermissionIntent(context, sliceUri, callingPackage), + new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build()) + .addText(getPermissionString(context, callingPackage), null) + .build()) + .addHints(Slice.HINT_LIST_ITEM) + .build(); + } + + /** + * @hide + */ + public static PendingIntent createPermissionIntent(Context context, Uri sliceUri, + String callingPackage) { + Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION); + intent.setComponent(new ComponentName("com.android.systemui", + "com.android.systemui.SlicePermissionActivity")); + intent.putExtra(EXTRA_BIND_URI, sliceUri); + intent.putExtra(EXTRA_PKG, callingPackage); + intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName()); + // Unique pending intent. + intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage) + .build()); + + return PendingIntent.getActivity(context, 0, intent, 0); + } + + /** + * @hide + */ + public static CharSequence getPermissionString(Context context, String callingPackage) { + PackageManager pm = context.getPackageManager(); + try { + return context.getString( + com.android.internal.R.string.slices_permission_request, + pm.getApplicationInfo(callingPackage, 0).loadLabel(pm), + context.getApplicationInfo().loadLabel(pm)); + } catch (NameNotFoundException e) { + // This shouldn't be possible since the caller is verified. + throw new RuntimeException("Unknown calling app", e); + } + } + + private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs, + String callingPackage) { ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); try { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyDeath() .build()); + mBindingPkg = callingPackage; return onBindSlice(sliceUri, supportedSpecs); } finally { + mBindingPkg = null; StrictMode.setThreadPolicy(oldPolicy); } } diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java index 2e44a630b056..da36157d85f8 100644 --- a/core/java/android/app/usage/NetworkStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -227,6 +227,30 @@ public final class NetworkStats implements AutoCloseable { */ public static final int ROAMING_YES = 0x2; + /** @hide */ + @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = { + DEFAULT_NETWORK_ALL, + DEFAULT_NETWORK_NO, + DEFAULT_NETWORK_YES + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DefaultNetwork {} + + /** + * Combined usage for this network regardless of whether it was the active default network. + */ + public static final int DEFAULT_NETWORK_ALL = -1; + + /** + * Usage that occurs while this network is not the active default network. + */ + public static final int DEFAULT_NETWORK_NO = 0x1; + + /** + * Usage that occurs while this network is the active default network. + */ + public static final int DEFAULT_NETWORK_YES = 0x2; + /** * Special TAG value for total data across all tags */ @@ -235,6 +259,7 @@ public final class NetworkStats implements AutoCloseable { private int mUid; private int mTag; private int mState; + private int mDefaultNetwork; private int mMetered; private int mRoaming; private long mBeginTimeStamp; @@ -286,6 +311,15 @@ public final class NetworkStats implements AutoCloseable { return 0; } + private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) { + switch (defaultNetwork) { + case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL; + case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO; + case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES; + } + return 0; + } + public Bucket() { } @@ -351,6 +385,21 @@ public final class NetworkStats implements AutoCloseable { } /** + * Default network state. One of the following values:<p/> + * <ul> + * <li>{@link #DEFAULT_NETWORK_ALL}</li> + * <li>{@link #DEFAULT_NETWORK_NO}</li> + * <li>{@link #DEFAULT_NETWORK_YES}</li> + * </ul> + * <p>Indicates whether the network usage occurred on the system default network for this + * type of traffic, or whether the application chose to send this traffic on a network that + * was not the one selected by the system. + */ + public @DefaultNetwork int getDefaultNetwork() { + return mDefaultNetwork; + } + + /** * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see * {@link java.lang.System#currentTimeMillis}. * @return Start of interval. @@ -551,6 +600,8 @@ public final class NetworkStats implements AutoCloseable { bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid); bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag); bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set); + bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork( + mRecycledSummaryEntry.defaultNetwork); bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered); bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming); bucketOut.mBeginTimeStamp = mStartTimeStamp; @@ -600,6 +651,7 @@ public final class NetworkStats implements AutoCloseable { bucketOut.mUid = Bucket.convertUid(getUid()); bucketOut.mTag = Bucket.convertTag(mTag); bucketOut.mState = Bucket.STATE_ALL; + bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL; bucketOut.mMetered = Bucket.METERED_ALL; bucketOut.mRoaming = Bucket.ROAMING_ALL; bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart; diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 853b00331a4d..90b514e272f4 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -60,10 +60,11 @@ import android.util.Log; * {@link #queryDetailsForUid} <p /> * {@link #queryDetails} <p /> * These queries do not aggregate over time but do aggregate over state, metered and roaming. - * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to - * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be - * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be - * {@link NetworkStats.Bucket#ROAMING_ALL}. + * Therefore there can be multiple buckets for a particular key. However, all Buckets will have + * {@code state} {@link NetworkStats.Bucket#STATE_ALL}, + * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * {@code metered } {@link NetworkStats.Bucket#METERED_ALL}, + * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}. * <p /> * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, @@ -136,7 +137,9 @@ public class NetworkStatsManager { * roaming. This means the bucket's start and end timestamp are going to be the same as the * 'startTime' and 'endTime' parameters. State is going to be * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL}, - * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL}, + * tag {@link NetworkStats.Bucket#TAG_NONE}, + * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered {@link NetworkStats.Bucket#METERED_ALL}, * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}. * * @param networkType As defined in {@link ConnectivityManager}, e.g. @@ -209,10 +212,10 @@ public class NetworkStatsManager { /** * Query network usage statistics summaries. Result filtered to include only uids belonging to * calling user. Result is aggregated over time, hence all buckets will have the same start and - * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets' - * start and end timestamps are going to be the same as the 'startTime' and 'endTime' - * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the - * same. + * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This + * means buckets' start and end timestamps are going to be the same as the 'startTime' and + * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to + * be the same. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -258,9 +261,10 @@ public class NetworkStatsManager { * belonging to calling user. Result is aggregated over state but not aggregated over time. * This means buckets' start and end timestamps are going to be between 'startTime' and * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the - * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be - * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be - * {@link NetworkStats.Bucket#ROAMING_ALL}. + * same as the 'uid' parameter and tag the same as 'tag' parameter. + * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and + * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't * interpolate across partial buckets. Since bucket length is in the order of hours, this * method cannot be used to measure data usage on a fine grained time scale. @@ -301,9 +305,10 @@ public class NetworkStatsManager { * metered, nor roaming. This means buckets' start and end timestamps are going to be between * 'startTime' and 'endTime' parameters. State is going to be * {@link NetworkStats.Bucket#STATE_ALL}, uid will vary, - * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be - * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be - * {@link NetworkStats.Bucket#ROAMING_ALL}. + * tag {@link NetworkStats.Bucket#TAG_NONE}, + * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, + * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't * interpolate across partial buckets. Since bucket length is in the order of hours, this * method cannot be used to measure data usage on a fine grained time scale. diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 5cd0981f72e3..bd978e3df863 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -176,6 +176,12 @@ public abstract class UsageStatsManagerInternal { public abstract void setActiveAdminApps(Set<String> adminApps, int userId); /** + * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and + * pushed to UsageStatsService. + */ + public abstract void onAdminDataAvailable(); + + /** * Return usage stats. * * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 3290d57f13a3..9f11d6e9a6aa 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -79,8 +79,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * {@link BluetoothDevice} objects representing all paired devices with * {@link #getBondedDevices()}; start device discovery with * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to - * listen for incoming connection requests with - * {@link #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for + * listen for incoming RFComm connection requests with {@link + * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented + * Channels (CoC) connection requests with listenUsingL2capCoc(int)}; or start a scan for * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}. * </p> * <p>This class is thread safe.</p> @@ -210,6 +211,14 @@ public final class BluetoothAdapter { public static final int STATE_BLE_TURNING_OFF = 16; /** + * UUID of the GATT Read Characteristics for LE_PSM value. + * + * @hide + */ + public static final UUID LE_PSM_CHARACTERISTIC_UUID = + UUID.fromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); + + /** * Human-readable string helper for AdapterState * * @hide @@ -1675,6 +1684,27 @@ public final class BluetoothAdapter { } /** + * Get the maximum number of connected audio devices. + * + * @return the maximum number of connected audio devices + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getMaxConnectedAudioDevices() { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getMaxConnectedAudioDevices(); + } + } catch (RemoteException e) { + Log.e(TAG, "failed to get getMaxConnectedAudioDevices, error: ", e); + } finally { + mServiceLock.readLock().unlock(); + } + return 1; + } + + /** * Return true if hardware has entries available for matching beacons * * @return true if there are hw entries available for matching beacons @@ -2139,7 +2169,9 @@ public final class BluetoothAdapter { min16DigitPin); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); + int assignedChannel = socket.mSocket.getPort(); + if (DBG) Log.d(TAG, "listenUsingL2capOn: set assigned channel to " + assignedChannel); + socket.setChannel(assignedChannel); } if (errno != 0) { //TODO(BT): Throw the same exception error code @@ -2180,12 +2212,18 @@ public final class BluetoothAdapter { * @hide */ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { + Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port); BluetoothServerSocket socket = new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false, - false); + false); int errno = socket.mSocket.bindListen(); if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { - socket.setChannel(socket.mSocket.getPort()); + int assignedChannel = socket.mSocket.getPort(); + if (DBG) { + Log.d(TAG, "listenUsingInsecureL2capOn: set assigned channel to " + + assignedChannel); + } + socket.setChannel(assignedChannel); } if (errno != 0) { //TODO(BT): Throw the same exception error code @@ -2744,4 +2782,103 @@ public final class BluetoothAdapter { scanner.stopScan(scanCallback); } } + + /** + * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and + * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen + * for incoming connections. + * <p>A remote device connecting to this socket will be authenticated and communication on this + * socket will be encrypted. + * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening + * {@link BluetoothServerSocket}. + * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link + * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is + * closed, Bluetooth is turned off, or the application exits unexpectedly. + * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is + * defined and performed by the application. + * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server + * socket from another Android device that is given the PSM value. + * + * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} + * @return an L2CAP CoC BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or unable to start this CoC + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingL2capCoc(int transport) + throws IOException { + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true, + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + throw new IOException("Error: " + errno); + } + + int assignedPsm = socket.mSocket.getPort(); + if (assignedPsm == 0) { + throw new IOException("Error: Unable to assign PSM value"); + } + if (DBG) { + Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to " + + assignedPsm); + } + socket.setChannel(assignedPsm); + + return socket; + } + + /** + * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and + * assign a dynamic PSM value. This socket can be used to listen for incoming connections. + * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable + * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and + * authenticated communication channel is desired. + * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening + * {@link BluetoothServerSocket}. + * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value + * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released + * when this server socket is closed, Bluetooth is turned off, or the application exits + * unexpectedly. + * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is + * defined and performed by the application. + * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this + * server socket from another Android device that is given the PSM value. + * + * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE} + * @return an L2CAP CoC BluetoothServerSocket + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions, or unable to start this CoC + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) + throws IOException { + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + BluetoothServerSocket socket = + new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false, + SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false); + int errno = socket.mSocket.bindListen(); + if (errno != 0) { + throw new IOException("Error: " + errno); + } + + int assignedPsm = socket.mSocket.getPort(); + if (assignedPsm == 0) { + throw new IOException("Error: Unable to assign PSM value"); + } + if (DBG) { + Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to " + + assignedPsm); + } + socket.setChannel(assignedPsm); + + return socket; + } } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 9b736b7e5f9e..ac21395c2fa7 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1921,4 +1921,75 @@ public final class BluetoothDevice implements Parcelable { } return null; } + + /** + * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can + * be used to start a secure outgoing connection to the remote device with the same dynamic + * protocol/service multiplexer (PSM) value. + * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for + * peer-peer Bluetooth applications. + * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. + * <p>Application using this API is responsible for obtaining PSM value from remote device. + * <p>The remote device will be authenticated and communication on this socket will be + * encrypted. + * <p> Use this socket if an authenticated socket link is possible. Authentication refers + * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a + * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int, + * int)}. + * + * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} + * @param psm dynamic PSM value from remote device + * @return a CoC #BluetoothSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { + if (!isBluetoothEnabled()) { + Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled"); + throw new IOException(); + } + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm); + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm, + null); + } + + /** + * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can + * be used to start a secure outgoing connection to the remote device with the same dynamic + * protocol/service multiplexer (PSM) value. + * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)} + * for peer-peer Bluetooth applications. + * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection. + * <p>Application using this API is responsible for obtaining PSM value from remote device. + * <p> The communication channel may not have an authenticated link key, i.e. it may be subject + * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and + * authenticated communication channel is possible. + * + * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE} + * @param psm dynamic PSM value from remote device + * @return a CoC #BluetoothSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or insufficient + * permissions + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { + if (!isBluetoothEnabled()) { + Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled"); + throw new IOException(); + } + if (transport != BluetoothDevice.TRANSPORT_LE) { + throw new IllegalArgumentException("Unsupported transport: " + transport); + } + if (DBG) { + Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm); + } + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm, + null); + } } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java index dc00d63043fc..d46b2e37467b 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -73,17 +73,18 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private final boolean mOutgoing; private final UUID mUUID; private final long mCreationElapsedMilli; + private final boolean mInBandRing; /** * Creates BluetoothHeadsetClientCall instance. */ public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, - boolean multiParty, boolean outgoing) { - this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing); + boolean multiParty, boolean outgoing, boolean inBandRing) { + this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing, inBandRing); } public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state, - String number, boolean multiParty, boolean outgoing) { + String number, boolean multiParty, boolean outgoing, boolean inBandRing) { mDevice = device; mId = id; mUUID = uuid; @@ -91,6 +92,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { mNumber = number != null ? number : ""; mMultiParty = multiParty; mOutgoing = outgoing; + mInBandRing = inBandRing; mCreationElapsedMilli = SystemClock.elapsedRealtime(); } @@ -200,6 +202,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { return mOutgoing; } + /** + * Checks if the ringtone will be generated by the connected phone + * + * @return <code>true</code> if in band ring is enabled, <code>false</code> otherwise. + */ + public boolean isInBandRing() { + return mInBandRing; + } + + @Override public String toString() { return toString(false); @@ -253,6 +265,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { builder.append(mMultiParty); builder.append(", mOutgoing: "); builder.append(mOutgoing); + builder.append(", mInBandRing: "); + builder.append(mInBandRing); builder.append("}"); return builder.toString(); } @@ -266,7 +280,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public BluetoothHeadsetClientCall createFromParcel(Parcel in) { return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null), in.readInt(), UUID.fromString(in.readString()), in.readInt(), - in.readString(), in.readInt() == 1, in.readInt() == 1); + in.readString(), in.readInt() == 1, in.readInt() == 1, + in.readInt() == 1); } @Override @@ -284,6 +299,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { out.writeString(mNumber); out.writeInt(mMultiParty ? 1 : 0); out.writeInt(mOutgoing ? 1 : 0); + out.writeInt(mInBandRing ? 1 : 0); } @Override diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 58d090dc287b..ebb7f187aea5 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -68,6 +68,7 @@ import java.io.IOException; public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; + private static final boolean DBG = false; /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; @@ -169,6 +170,7 @@ public final class BluetoothServerSocket implements Closeable { * close any {@link BluetoothSocket} received from {@link #accept()}. */ public void close() throws IOException { + if (DBG) Log.d(TAG, "BluetoothServerSocket:close() called. mChannel=" + mChannel); synchronized (this) { if (mHandler != null) { mHandler.obtainMessage(mMessage).sendToTarget(); @@ -197,6 +199,20 @@ public final class BluetoothServerSocket implements Closeable { } /** + * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP + * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the + * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link + * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this + * method is called on non-L2CAP server sockets. + * + * @return the assigned PSM or LE_PSM value depending on transport + * @hide + */ + public int getPsm() { + return mChannel; + } + + /** * Sets the channel on which future sockets are bound. * Currently used only when a channel is auto generated. */ @@ -227,6 +243,10 @@ public final class BluetoothServerSocket implements Closeable { sb.append("TYPE_L2CAP"); break; } + case BluetoothSocket.TYPE_L2CAP_LE: { + sb.append("TYPE_L2CAP_LE"); + break; + } case BluetoothSocket.TYPE_SCO: { sb.append("TYPE_SCO"); break; diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 0569913435a8..09f96840f918 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -99,6 +99,16 @@ public final class BluetoothSocket implements Closeable { /** L2CAP socket */ public static final int TYPE_L2CAP = 3; + /** L2CAP socket on BR/EDR transport + * @hide + */ + public static final int TYPE_L2CAP_BREDR = TYPE_L2CAP; + + /** L2CAP socket on LE transport + * @hide + */ + public static final int TYPE_L2CAP_LE = 4; + /*package*/ static final int EBADFD = 77; /*package*/ static final int EADDRINUSE = 98; @@ -417,6 +427,7 @@ public final class BluetoothSocket implements Closeable { return -1; } try { + if (DBG) Log.d(TAG, "bindListen(): mPort=" + mPort + ", mType=" + mType); mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName, mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { @@ -451,7 +462,7 @@ public final class BluetoothSocket implements Closeable { mSocketState = SocketState.LISTENING; } } - if (DBG) Log.d(TAG, "channel: " + channel); + if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort); if (mPort <= -1) { mPort = channel; } // else ASSERT(mPort == channel) @@ -515,7 +526,7 @@ public final class BluetoothSocket implements Closeable { /*package*/ int read(byte[] b, int offset, int length) throws IOException { int ret = 0; if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length); - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { int bytesToRead = length; if (VDBG) { Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length @@ -558,7 +569,7 @@ public final class BluetoothSocket implements Closeable { // Rfcomm uses dynamic allocation, and should not have any bindings // to the actual message length. if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length); - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { if (length <= mMaxTxPacketSize) { mSocketOS.write(b, offset, length); } else { @@ -702,7 +713,7 @@ public final class BluetoothSocket implements Closeable { } private void createL2capRxBuffer() { - if (mType == TYPE_L2CAP) { + if ((mType == TYPE_L2CAP) || (mType == TYPE_L2CAP_LE)) { // Allocate the buffer to use for reads. if (VDBG) Log.v(TAG, " Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize); mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index e374bbc1c017..f69aab01d821 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4158,7 +4158,7 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations. + * {@link android.content.pm.CrossProfileApps} for cross profile operations. * * @see #getSystemService(String) */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6e9970980e96..f05a4d1aa9c7 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3516,12 +3516,83 @@ public class Intent implements Parcelable, Cloneable { * For more details see TelephonyIntents.ACTION_SIM_STATE_CHANGED. This is here * because TelephonyIntents is an internal class. * @hide + * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or + * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} */ + @Deprecated @SystemApi @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; /** + * Broadcast Action: The sim card state has changed. + * The intent will have the following extra values:</p> + * <dl> + * <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt> + * <dd>The sim card state. One of: + * <dl> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_ABSENT}</dt> + * <dd>SIM card not found</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_IO_ERROR}</dt> + * <dd>SIM card IO error</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_RESTRICTED}</dt> + * <dd>SIM card is restricted</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PRESENT}</dt> + * <dd>SIM card is present</dd> + * </dl> + * </dd> + * </dl> + * + * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + * <p class="note">The current state can also be queried using + * {@link android.telephony.TelephonyManager.getSimCardState()} + * + * <p class="note">This is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_CARD_STATE_CHANGED = + "android.intent.action.SIM_CARD_STATE_CHANGED"; + + /** + * Broadcast Action: The sim application state has changed. + * The intent will have the following extra values:</p> + * <dl> + * <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt> + * <dd>The sim application state. One of: + * <dl> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NOT_READY}</dt> + * <dd>SIM card applications not ready</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED}</dt> + * <dd>SIM card PIN locked</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED}</dt> + * <dd>SIM card PUK locked</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NETWORK_LOCKED}</dt> + * <dd>SIM card network locked</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED}</dt> + * <dd>SIM card permanently disabled due to PUK failures</dd> + * <dt>{@link android.telephony.TelephonyManager.SIM_STATE_LOADED}</dt> + * <dd>SIM card data loaded</dd> + * </dl> + * </dd> + * </dl> + * + * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission. + * + * <p class="note">The current state can also be queried using + * {@link android.telephony.TelephonyManager.getSimApplicationState()} + * + * <p class="note">This is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = + "android.intent.action.SIM_APPLICATION_STATE_CHANGED"; + + /** * Broadcast Action: indicate that the phone service state has changed. * The intent will have the following extra values:</p> * <p> diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 15e119b20a2e..84cbdb472c13 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -26,7 +26,6 @@ import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; @@ -1593,11 +1592,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } - /** @hide */ - public boolean isTargetingDeprecatedSdkVersion() { - return targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT; - } - /** * Returns whether or not this application was installed as a virtual preload. */ diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 414c13894f80..7d5d6090788c 100644 --- a/core/java/android/content/pm/crossprofile/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.content.pm.crossprofile; +package android.content.pm; import android.annotation.NonNull; import android.content.ComponentName; @@ -57,13 +57,14 @@ public class CrossProfileApps { * action {@link android.content.Intent#ACTION_MAIN}, category * {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will * be thrown. - * @param user The UserHandle of the profile, must be one of the users returned by + * @param targetUser The UserHandle of the profile, must be one of the users returned by * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will * be thrown. */ - public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user) { + public void startMainActivity(@NonNull ComponentName component, + @NonNull UserHandle targetUser) { try { - mService.startActivityAsUser(mContext.getPackageName(), component, user); + mService.startActivityAsUser(mContext.getPackageName(), component, targetUser); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -114,7 +115,7 @@ public class CrossProfileApps { } /** - * Return an icon that calling app can show to user for the semantic of profile switching -- + * Return a drawable that calling app can show to user for the semantic of profile switching -- * launching its own activity in specified user profile. For example, it may return a briefcase * icon if the given user handle is the managed profile one. * @@ -124,9 +125,9 @@ public class CrossProfileApps { * @return an icon that calling app can show user for the semantic of launching its own * activity in specified user profile. * - * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle) + * @see #startMainActivity(ComponentName, UserHandle) */ - public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) { + public @NonNull Drawable getProfileSwitchingIconDrawable(@NonNull UserHandle userHandle) { verifyCanAccessUser(userHandle); final boolean isManagedProfile = diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 227f91f5d3e1..e79deb96838b 100644 --- a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.crossprofile; +package android.content.pm; import android.content.ComponentName; import android.content.Intent; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 44b4a33976d0..bcf80eeaaee7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1297,6 +1297,15 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116; + /** + * Installation parse return code: this is passed in the + * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the dex metadata file is invalid or + * if there was no matching apk file for a dex metadata file. + * + * @hide + */ + public static final int INSTALL_FAILED_BAD_DEX_METADATA = -117; + /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, @@ -5618,6 +5627,8 @@ public abstract class PackageManager { case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION"; case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS"; case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; + case INSTALL_FAILED_BAD_DEX_METADATA: + return "INSTALL_FAILED_BAD_DEX_METADATA"; default: return Integer.toString(status); } } @@ -5662,6 +5673,7 @@ public abstract class PackageManager { case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID; + case INSTALL_FAILED_BAD_DEX_METADATA: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE; case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT; diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 2c45b8d8b30e..6f093ba8d005 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -436,6 +436,11 @@ public abstract class PackageManagerInternal { */ public abstract int getUidTargetSdkVersion(int uid); + /** + * Return the taget SDK version for the app with the given package name. + */ + public abstract int getPackageTargetSdkVersion(String packageName); + /** Whether the binder caller can access instant apps. */ public abstract boolean canAccessInstantApps(int callingUid, int userId); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 6d6c02a47082..4a71467f4b36 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -240,6 +240,9 @@ public class PackageParser { } /** @hide */ + public static final String APK_FILE_EXTENSION = ".apk"; + + /** @hide */ public static class NewPermissionInfo { public final String name; public final int sdkVersion; @@ -613,7 +616,7 @@ public class PackageParser { } public static boolean isApkPath(String path) { - return path.endsWith(".apk"); + return path.endsWith(APK_FILE_EXTENSION); } /** diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java new file mode 100644 index 000000000000..c5f1c852c7b2 --- /dev/null +++ b/core/java/android/content/pm/dex/DexMetadataHelper.java @@ -0,0 +1,230 @@ +/** + * 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.content.pm.dex; + +import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA; +import static android.content.pm.PackageParser.APK_FILE_EXTENSION; + +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageLite; +import android.content.pm.PackageParser.PackageParserException; +import android.util.ArrayMap; +import android.util.jar.StrictJarFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Helper class used to compute and validate the location of dex metadata files. + * + * @hide + */ +public class DexMetadataHelper { + private static final String DEX_METADATA_FILE_EXTENSION = ".dm"; + + private DexMetadataHelper() {} + + /** Return true if the given file is a dex metadata file. */ + public static boolean isDexMetadataFile(File file) { + return isDexMetadataPath(file.getName()); + } + + /** Return true if the given path is a dex metadata path. */ + private static boolean isDexMetadataPath(String path) { + return path.endsWith(DEX_METADATA_FILE_EXTENSION); + } + + /** + * Return the size (in bytes) of all dex metadata files associated with the given package. + */ + public static long getPackageDexMetadataSize(PackageLite pkg) { + long sizeBytes = 0; + Collection<String> dexMetadataList = DexMetadataHelper.getPackageDexMetadata(pkg).values(); + for (String dexMetadata : dexMetadataList) { + sizeBytes += new File(dexMetadata).length(); + } + return sizeBytes; + } + + /** + * Search for the dex metadata file associated with the given target file. + * If it exists, the method returns the dex metadata file; otherwise it returns null. + * + * Note that this performs a loose matching suitable to be used in the InstallerSession logic. + * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile} + * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk'). + */ + public static File findDexMetadataForFile(File targetFile) { + String dexMetadataPath = buildDexMetadataPathForFile(targetFile); + File dexMetadataFile = new File(dexMetadataPath); + return dexMetadataFile.exists() ? dexMetadataFile : null; + } + + /** + * Return the dex metadata files for the given package as a map + * [code path -> dex metadata path]. + * + * NOTE: involves I/O checks. + */ + public static Map<String, String> getPackageDexMetadata(PackageParser.Package pkg) { + return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths()); + } + + /** + * Return the dex metadata files for the given package as a map + * [code path -> dex metadata path]. + * + * NOTE: involves I/O checks. + */ + private static Map<String, String> getPackageDexMetadata(PackageLite pkg) { + return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths()); + } + + /** + * Look up the dex metadata files for the given code paths building the map + * [code path -> dex metadata]. + * + * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists. + * If it does it adds the pair to the returned map. + * + * Note that this method will do a strict + * matching based on the extension ('foo.dm' will only match 'foo.apk'). + * + * This should only be used for code paths extracted from a package structure after the naming + * was enforced in the installer. + */ + private static Map<String, String> buildPackageApkToDexMetadataMap( + List<String> codePaths) { + ArrayMap<String, String> result = new ArrayMap<>(); + for (int i = codePaths.size() - 1; i >= 0; i--) { + String codePath = codePaths.get(i); + String dexMetadataPath = buildDexMetadataPathForApk(codePath); + + if (Files.exists(Paths.get(dexMetadataPath))) { + result.put(codePath, dexMetadataPath); + } + } + + return result; + } + + /** + * Return the dex metadata path associated with the given code path. + * (replaces '.apk' extension with '.dm') + * + * @throws IllegalArgumentException if the code path is not an .apk. + */ + public static String buildDexMetadataPathForApk(String codePath) { + if (!PackageParser.isApkPath(codePath)) { + throw new IllegalStateException( + "Corrupted package. Code path is not an apk " + codePath); + } + return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length()) + + DEX_METADATA_FILE_EXTENSION; + } + + /** + * Return the dex metadata path corresponding to the given {@code targetFile} using a loose + * matching. + * i.e. the method will attempt to match the {@code dmFile} regardless of {@code targetFile} + * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk'). + */ + private static String buildDexMetadataPathForFile(File targetFile) { + return PackageParser.isApkFile(targetFile) + ? buildDexMetadataPathForApk(targetFile.getPath()) + : targetFile.getPath() + DEX_METADATA_FILE_EXTENSION; + } + + /** + * Validate the dex metadata files installed for the given package. + * + * @throws PackageParserException in case of errors. + */ + public static void validatePackageDexMetadata(PackageParser.Package pkg) + throws PackageParserException { + Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values(); + for (String dexMetadata : apkToDexMetadataList) { + validateDexMetadataFile(dexMetadata); + } + } + + /** + * Validate that the given file is a dex metadata archive. + * This is just a sanity validation that the file is a zip archive. + * + * @throws PackageParserException if the file is not a .dm file. + */ + private static void validateDexMetadataFile(String dmaPath) throws PackageParserException { + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(dmaPath, false, false); + } catch (IOException e) { + throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA, + "Error opening " + dmaPath, e); + } finally { + if (jarFile != null) { + try { + jarFile.close(); + } catch (IOException ignored) { + } + } + } + } + + /** + * Validates that all dex metadata paths in the given list have a matching apk. + * (for any foo.dm there should be either a 'foo' of a 'foo.apk' file). + * If that's not the case it throws {@code IllegalStateException}. + * + * This is used to perform a basic sanity check during adb install commands. + * (The installer does not support stand alone .dm files) + */ + public static void validateDexPaths(String[] paths) { + ArrayList<String> apks = new ArrayList<>(); + for (int i = 0; i < paths.length; i++) { + if (PackageParser.isApkPath(paths[i])) { + apks.add(paths[i]); + } + } + ArrayList<String> unmatchedDmFiles = new ArrayList<>(); + for (int i = 0; i < paths.length; i++) { + String dmPath = paths[i]; + if (isDexMetadataPath(dmPath)) { + boolean valid = false; + for (int j = apks.size() - 1; j >= 0; j--) { + if (dmPath.equals(buildDexMetadataPathForFile(new File(apks.get(j))))) { + valid = true; + break; + } + } + if (!valid) { + unmatchedDmFiles.add(dmPath); + } + } + } + if (!unmatchedDmFiles.isEmpty()) { + throw new IllegalStateException("Unmatched .dm files: " + unmatchedDmFiles); + } + } + +} diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 49f357e6cb10..a2991e6e9cb6 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -66,7 +66,7 @@ public abstract class SQLiteOpenHelper { * created or opened until one of {@link #getWritableDatabase} or * {@link #getReadableDatabase} is called. * - * @param context to use to open or create the database + * @param context to use for locating paths to the the database * @param name of the database file, or null for an in-memory database * @param factory to use for creating cursor objects, or null for the default * @param version number of the database (starting at 1); if the database is older, @@ -86,7 +86,7 @@ public abstract class SQLiteOpenHelper { * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be * used to handle corruption when sqlite reports database corruption.</p> * - * @param context to use to open or create the database + * @param context to use for locating paths to the the database * @param name of the database file, or null for an in-memory database * @param factory to use for creating cursor objects, or null for the default * @param version number of the database (starting at 1); if the database is older, @@ -107,7 +107,7 @@ public abstract class SQLiteOpenHelper { * created or opened until one of {@link #getWritableDatabase} or * {@link #getReadableDatabase} is called. * - * @param context to use to open or create the database + * @param context to use for locating paths to the the database * @param name of the database file, or null for an in-memory database * @param version number of the database (starting at 1); if the database is older, * {@link #onUpgrade} will be used to upgrade the database; if the database is @@ -128,7 +128,7 @@ public abstract class SQLiteOpenHelper { * minimumSupportedVersion is found, it is simply deleted and a new database is created with the * given name and version * - * @param context to use to open or create the database + * @param context to use for locating paths to the the database * @param name the name of the database file, null for a temporary in-memory database * @param factory to use for creating cursor objects, null for default * @param version the required version of the database diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index 7866b52cc68c..9aa3f40a8f0a 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -25,11 +25,11 @@ import android.os.Parcelable; import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; +import libcore.util.NativeAllocationRegistry; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import libcore.util.NativeAllocationRegistry; - /** * HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object * representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing @@ -42,18 +42,25 @@ import libcore.util.NativeAllocationRegistry; public final class HardwareBuffer implements Parcelable, AutoCloseable { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "RGB", "BLOB" }, value = { + @IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = { RGBA_8888, RGBA_FP16, RGBA_1010102, RGBX_8888, RGB_888, RGB_565, - BLOB + BLOB, + D_16, + D_24, + DS_24UI8, + D_FP32, + DS_FP32UI8, + S_UI8, }) public @interface Format { } + @Format /** Format: 8 bits each red, green, blue, alpha */ public static final int RGBA_8888 = 1; /** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */ @@ -68,6 +75,18 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { public static final int RGBA_1010102 = 0x2b; /** Format: opaque format used for raw data transfer; must have a height of 1 */ public static final int BLOB = 0x21; + /** Format: 16 bits depth */ + public static final int D_16 = 0x30; + /** Format: 24 bits depth */ + public static final int D_24 = 0x31; + /** Format: 24 bits depth, 8 bits stencil */ + public static final int DS_24UI8 = 0x32; + /** Format: 32 bits depth */ + public static final int D_FP32 = 0x33; + /** Format: 32 bits depth, 8 bits stencil */ + public static final int DS_FP32UI8 = 0x34; + /** Format: 8 bits stencil */ + public static final int S_UI8 = 0x35; // Note: do not rename, this field is used by native code private long mNativeObject; @@ -82,9 +101,11 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN, USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE, USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE, - USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA}) + USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP, + USAGE_GPU_MIPMAP_COMPLETE}) public @interface Usage {}; + @Usage /** Usage: The buffer will sometimes be read by the CPU */ public static final long USAGE_CPU_READ_RARELY = 2; /** Usage: The buffer will often be read by the CPU */ @@ -107,6 +128,10 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { public static final long USAGE_SENSOR_DIRECT_DATA = 1 << 23; /** Usage: The buffer will be used as a shader storage or uniform buffer object */ public static final long USAGE_GPU_DATA_BUFFER = 1 << 24; + /** Usage: The buffer will be used as a cube map texture */ + public static final long USAGE_GPU_CUBE_MAP = 1 << 25; + /** Usage: The buffer contains a complete mipmap hierarchy */ + public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26; // The approximate size of a native AHardwareBuffer object. private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232; @@ -118,15 +143,9 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { * * @param width The width in pixels of the buffer * @param height The height in pixels of the buffer - * @param format The format of each pixel, one of {@link #RGBA_8888}, {@link #RGBA_FP16}, - * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB} + * @param format The @Format of each pixel * @param layers The number of layers in the buffer - * @param usage Flags describing how the buffer will be used, one of - * {@link #USAGE_CPU_READ_RARELY}, {@link #USAGE_CPU_READ_OFTEN}, - * {@link #USAGE_CPU_WRITE_RARELY}, {@link #USAGE_CPU_WRITE_OFTEN}, - * {@link #USAGE_GPU_SAMPLED_IMAGE}, {@link #USAGE_GPU_COLOR_OUTPUT}, - * {@link #USAGE_GPU_DATA_BUFFER}, {@link #USAGE_PROTECTED_CONTENT}, - * {@link #USAGE_SENSOR_DIRECT_DATA}, {@link #USAGE_VIDEO_ENCODE} + * @param usage The @Usage flags describing how the buffer will be used * @return A <code>HardwareBuffer</code> instance if successful, or throws an * IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or * too large to allocate), if the format is not supported, if the requested number of layers @@ -154,7 +173,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { if (nativeObject == 0) { throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " + "dimensions passed were too large, too many image layers were requested, " + - "or an invalid set of usage flags was passed"); + "or an invalid set of usage flags or invalid format was passed"); } return new HardwareBuffer(nativeObject); } @@ -206,8 +225,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { } /** - * Returns the format of this buffer, one of {@link #RGBA_8888}, {@link #RGBA_FP16}, - * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}. + * Returns the @Format of this buffer. */ @Format public int getFormat() { @@ -338,6 +356,12 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { case RGB_565: case RGB_888: case BLOB: + case D_16: + case D_24: + case DS_24UI8: + case D_FP32: + case DS_FP32UI8: + case S_UI8: return true; } return false; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1201ef48220a..a9ed9a3858c0 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2978,6 +2978,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li> * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li> + * <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}</li> * </ul></p> * <p>This key is available on all devices.</p> * @@ -2991,6 +2992,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3 + * @see #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL */ @PublicKey public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL = diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index ce1fba798c0e..639795ab8080 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -183,7 +183,9 @@ public abstract class CameraDevice implements AutoCloseable { TEMPLATE_RECORD, TEMPLATE_VIDEO_SNAPSHOT, TEMPLATE_ZERO_SHUTTER_LAG, - TEMPLATE_MANUAL }) + TEMPLATE_MANUAL, + TEMPLATE_MOTION_TRACKING_PREVIEW, + TEMPLATE_MOTION_TRACKING_BEST}) public @interface RequestTemplate {}; /** @@ -424,14 +426,17 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output - * aspect ratio. So for a device with a 4:3 image sensor, this will be 640x480, and for a - * device with a 16:9 sensor, this will be 640x360, and so on. + * aspect ratio, rounded down. So for a device with a 4:3 image sensor, this will be 640x480, + * and for a device with a 16:9 sensor, this will be 640x360, and so on. And the + * {@code MAX 30FPS} entry means the largest JPEG resolution on the device for which + * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration} + * returns a value less than or equal to 1/30s. * * <table> * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr> * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr> * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> - * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>Live preview with a tracking YUV output and a maximum-resolution YUV for still captures.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code JPEG}</td><td id="rb">{@code MAX 30FPS}</td> <td>Preview with a tracking YUV output and a as-large-as-possible JPEG for still captures.</td> </tr> * </table><br> * </p> * @@ -899,12 +904,6 @@ public abstract class CameraDevice implements AutoCloseable { * @throws CameraAccessException if the camera device is no longer connected or has * encountered a fatal error * @throws IllegalStateException if the camera device has been closed - * - * @see #TEMPLATE_PREVIEW - * @see #TEMPLATE_RECORD - * @see #TEMPLATE_STILL_CAPTURE - * @see #TEMPLATE_VIDEO_SNAPSHOT - * @see #TEMPLATE_MANUAL */ @NonNull public abstract CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType) diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 1c7f2896b167..cc6e9a964271 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1134,6 +1134,38 @@ public abstract class CameraMetadata<TKey> { */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; + /** + * <p>This camera device is backed by an external camera connected to this Android device.</p> + * <p>The device has capability identical to a LIMITED level device, with the following + * exceptions:</p> + * <ul> + * <li>The device may not report lens/sensor related information such as<ul> + * <li>{@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}</li> + * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li> + * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li> + * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li> + * <li>{@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew}</li> + * </ul> + * </li> + * <li>The device will report 0 for {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}</li> + * <li>The device has less guarantee on stable framerate, as the framerate partly depends + * on the external camera being used.</li> + * </ul> + * + * @see CaptureRequest#LENS_FOCAL_LENGTH + * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE + * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN + * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT + * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE + * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL + * @see CameraCharacteristics#SENSOR_ORIENTATION + * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + */ + public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; + // // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY // @@ -1359,6 +1391,20 @@ public abstract class CameraMetadata<TKey> { */ public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; + /** + * <p>An external flash has been turned on.</p> + * <p>It informs the camera device that an external flash has been turned on, and that + * metering (and continuous focus if active) should be quickly recaculated to account + * for the external flash. Otherwise, this mode acts like ON.</p> + * <p>When the external flash is turned off, AE mode should be changed to one of the + * other available AE modes.</p> + * <p>If the camera device supports AE external flash mode, aeState must be + * FLASH_REQUIRED after the camera device finishes AE scan and it's too dark without + * flash.</p> + * @see CaptureRequest#CONTROL_AE_MODE + */ + public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5; + // // Enumeration values for CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index cf27c704f7e5..ce75fa52eff7 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1076,6 +1076,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <li>{@link #CONTROL_AE_MODE_ON_AUTO_FLASH ON_AUTO_FLASH}</li> * <li>{@link #CONTROL_AE_MODE_ON_ALWAYS_FLASH ON_ALWAYS_FLASH}</li> * <li>{@link #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE ON_AUTO_FLASH_REDEYE}</li> + * <li>{@link #CONTROL_AE_MODE_ON_EXTERNAL_FLASH ON_EXTERNAL_FLASH}</li> * </ul></p> * <p><b>Available values for this device:</b><br> * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}</p> @@ -1093,6 +1094,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @see #CONTROL_AE_MODE_ON_AUTO_FLASH * @see #CONTROL_AE_MODE_ON_ALWAYS_FLASH * @see #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE + * @see #CONTROL_AE_MODE_ON_EXTERNAL_FLASH */ @PublicKey public static final Key<Integer> CONTROL_AE_MODE = diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index b6b0c907a8f9..237a92d3ca9f 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -691,6 +691,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <li>{@link #CONTROL_AE_MODE_ON_AUTO_FLASH ON_AUTO_FLASH}</li> * <li>{@link #CONTROL_AE_MODE_ON_ALWAYS_FLASH ON_ALWAYS_FLASH}</li> * <li>{@link #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE ON_AUTO_FLASH_REDEYE}</li> + * <li>{@link #CONTROL_AE_MODE_ON_EXTERNAL_FLASH ON_EXTERNAL_FLASH}</li> * </ul></p> * <p><b>Available values for this device:</b><br> * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}</p> @@ -708,6 +709,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * @see #CONTROL_AE_MODE_ON_AUTO_FLASH * @see #CONTROL_AE_MODE_ON_ALWAYS_FLASH * @see #CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE + * @see #CONTROL_AE_MODE_ON_EXTERNAL_FLASH */ @PublicKey public static final Key<Integer> CONTROL_AE_MODE = @@ -877,7 +879,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * </tr> * </tbody> * </table> - * <p>When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON_*:</p> + * <p>When {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is AE_MODE_ON*:</p> * <table> * <thead> * <tr> @@ -998,10 +1000,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * </tr> * </tbody> * </table> + * <p>If the camera device supports AE external flash mode (ON_EXTERNAL_FLASH is included in + * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}), aeState must be FLASH_REQUIRED after the camera device + * finishes AE scan and it's too dark without flash.</p> * <p>For the above table, the camera device may skip reporting any state changes that happen * without application intervention (i.e. mode switch, trigger, locking). Any state that * can be skipped in that manner is called a transient state.</p> - * <p>For example, for above AE modes (AE_MODE_ON_*), in addition to the state transitions + * <p>For example, for above AE modes (AE_MODE_ON*), in addition to the state transitions * listed in above table, it is also legal for the camera device to skip one or more * transient states between two results. See below table for examples:</p> * <table> @@ -1072,6 +1077,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * + * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES * @see CaptureRequest#CONTROL_AE_LOCK * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index f1ffb890eecd..4455d45d4160 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -16,9 +16,8 @@ package android.hardware.camera2.impl; -import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; +import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; -import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -31,7 +30,6 @@ import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.OutputConfiguration; -import android.hardware.camera2.params.ReprocessFormatsMap; import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SubmitInfo; @@ -1798,34 +1796,36 @@ public class CameraDeviceImpl extends CameraDevice case ERROR_CAMERA_DISCONNECTED: CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); break; - default: - Log.e(TAG, "Unknown error from camera device: " + errorCode); - // no break - case ERROR_CAMERA_DEVICE: - case ERROR_CAMERA_SERVICE: - mInError = true; - final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ? - StateCallback.ERROR_CAMERA_DEVICE : - StateCallback.ERROR_CAMERA_SERVICE; - Runnable r = new Runnable() { - @Override - public void run() { - if (!CameraDeviceImpl.this.isClosed()) { - mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode); - } - } - }; - CameraDeviceImpl.this.mDeviceHandler.post(r); - break; case ERROR_CAMERA_REQUEST: case ERROR_CAMERA_RESULT: case ERROR_CAMERA_BUFFER: onCaptureErrorLocked(errorCode, resultExtras); break; + case ERROR_CAMERA_DEVICE: + scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE); + break; + case ERROR_CAMERA_DISABLED: + scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED); + break; + default: + Log.e(TAG, "Unknown error from camera device: " + errorCode); + scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE); } } } + private void scheduleNotifyError(int code) { + mInError = true; + CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable( + CameraDeviceCallbacks::notifyError, this, code)); + } + + private void notifyError(int code) { + if (!CameraDeviceImpl.this.isClosed()) { + mDeviceCallback.onError(CameraDeviceImpl.this, code); + } + } + @Override public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { if (DEBUG) { diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 2156491a3e24..67e97bfd3d62 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.os.Parcel; @@ -25,6 +26,7 @@ import android.util.Pair; import com.android.internal.util.Preconditions; import java.util.Arrays; +import java.util.Objects; /** @hide */ @SystemApi @@ -32,10 +34,12 @@ import java.util.Arrays; public final class BrightnessConfiguration implements Parcelable { private final float[] mLux; private final float[] mNits; + private final String mDescription; - private BrightnessConfiguration(float[] lux, float[] nits) { + private BrightnessConfiguration(float[] lux, float[] nits, String description) { mLux = lux; mNits = nits; + mDescription = description; } /** @@ -51,10 +55,19 @@ public final class BrightnessConfiguration implements Parcelable { return Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length)); } + /** + * Returns description string. + * @hide + */ + public String getDescription() { + return mDescription; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloatArray(mLux); dest.writeFloatArray(mNits); + dest.writeString(mDescription); } @Override @@ -72,7 +85,9 @@ public final class BrightnessConfiguration implements Parcelable { } sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")"); } - sb.append("]}"); + sb.append("], '"); + sb.append(mDescription); + sb.append("'}"); return sb.toString(); } @@ -81,6 +96,7 @@ public final class BrightnessConfiguration implements Parcelable { int result = 1; result = result * 31 + Arrays.hashCode(mLux); result = result * 31 + Arrays.hashCode(mNits); + result = result * 31 + mDescription.hashCode(); return result; } @@ -93,16 +109,17 @@ public final class BrightnessConfiguration implements Parcelable { return false; } final BrightnessConfiguration other = (BrightnessConfiguration) o; - return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits); + return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits) + && Objects.equals(mDescription, other.mDescription); } public static final Creator<BrightnessConfiguration> CREATOR = new Creator<BrightnessConfiguration>() { public BrightnessConfiguration createFromParcel(Parcel in) { - Builder builder = new Builder(); float[] lux = in.createFloatArray(); float[] nits = in.createFloatArray(); - builder.setCurve(lux, nits); + Builder builder = new Builder(lux, nits); + builder.setDescription(in.readString()); return builder.build(); } @@ -117,6 +134,29 @@ public final class BrightnessConfiguration implements Parcelable { public static class Builder { private float[] mCurveLux; private float[] mCurveNits; + private String mDescription; + + /** + * STOPSHIP remove when app has stopped using this. + * @hide + */ + public Builder() { + } + + /** + * Constructs the builder with the control points for the brightness curve. + * + * Brightness curves must have strictly increasing ambient brightness values in lux and + * monotonically increasing display brightness values in nits. In addition, the initial + * control point must be 0 lux. + * + * @throws IllegalArgumentException if the initial control point is not at 0 lux. + * @throws IllegalArgumentException if the lux levels are not strictly increasing. + * @throws IllegalArgumentException if the nit levels are not monotonically increasing. + */ + public Builder(float[] lux, float[] nits) { + setCurve(lux, nits); + } /** * Sets the control points for the brightness curve. @@ -128,6 +168,9 @@ public final class BrightnessConfiguration implements Parcelable { * @throws IllegalArgumentException if the initial control point is not at 0 lux. * @throws IllegalArgumentException if the lux levels are not strictly increasing. * @throws IllegalArgumentException if the nit levels are not monotonically increasing. + * + * STOPSHIP remove when app has stopped using this. + * @hide */ public Builder setCurve(float[] lux, float[] nits) { Preconditions.checkNotNull(lux); @@ -151,6 +194,17 @@ public final class BrightnessConfiguration implements Parcelable { } /** + * Set description of the brightness curve. + * + * @param description brief text describing the curve pushed. It maybe truncated + * and will not be displayed in the UI + */ + public Builder setDescription(@Nullable String description) { + mDescription = description; + return this; + } + + /** * Builds the {@link BrightnessConfiguration}. * * A brightness curve <b>must</b> be set before calling this. @@ -159,7 +213,7 @@ public final class BrightnessConfiguration implements Parcelable { if (mCurveLux == null || mCurveNits == null) { throw new IllegalStateException("A curve must be set!"); } - return new BrightnessConfiguration(mCurveLux, mCurveNits); + return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription); } private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) { diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 76ab35d02c95..36673cd66ca2 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -639,7 +639,7 @@ public final class DisplayManager { @TestApi @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(BrightnessConfiguration c) { - setBrightnessConfigurationForUser(c, UserHandle.myUserId()); + setBrightnessConfigurationForUser(c, UserHandle.myUserId(), mContext.getPackageName()); } /** @@ -650,8 +650,9 @@ public final class DisplayManager { * * @hide */ - public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) { - mGlobal.setBrightnessConfigurationForUser(c, userId); + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, + String packageName) { + mGlobal.setBrightnessConfigurationForUser(c, userId, packageName); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index cbb5a7de7db8..9c851f1fd106 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -480,9 +480,10 @@ public final class DisplayManagerGlobal { * * @hide */ - public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) { + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, + String packageName) { try { - mDm.setBrightnessConfigurationForUser(c, userId); + mDm.setBrightnessConfigurationForUser(c, userId, packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 61c42e1ab491..5b7b32fe5201 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -90,5 +90,6 @@ interface IDisplayManager { // Sets the global brightness configuration for a given user. Requires // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not // the same as the calling user. - void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId); + void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId, + String packageName); } diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java index 2a4ad0013d4b..f85ce3eed82e 100644 --- a/core/java/android/hardware/location/ContextHubMessage.java +++ b/core/java/android/hardware/location/ContextHubMessage.java @@ -34,12 +34,11 @@ import java.util.Arrays; @SystemApi @Deprecated public class ContextHubMessage { + private static final int DEBUG_LOG_NUM_BYTES = 16; private int mType; private int mVersion; private byte[]mData; - private static final String TAG = "ContextHubMessage"; - /** * Get the message type * @@ -136,4 +135,28 @@ public class ContextHubMessage { return new ContextHubMessage[size]; } }; + + @Override + public String toString() { + int length = mData.length; + + String ret = + "ContextHubMessage[type = " + mType + ", length = " + mData.length + " bytes]("; + if (length > 0) { + ret += "data = 0x"; + } + for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) { + ret += Byte.toHexString(mData[i], true /* upperCase */); + + if ((i + 1) % 4 == 0) { + ret += " "; + } + } + if (length > DEBUG_LOG_NUM_BYTES) { + ret += "..."; + } + ret += ")"; + + return ret; + } } diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java index 716a1946c540..66352581dac3 100644 --- a/core/java/android/hardware/location/NanoAppMessage.java +++ b/core/java/android/hardware/location/NanoAppMessage.java @@ -28,6 +28,7 @@ import android.os.Parcelable; */ @SystemApi public final class NanoAppMessage implements Parcelable { + private static final int DEBUG_LOG_NUM_BYTES = 16; private long mNanoAppId; private int mMessageType; private byte[] mMessageBody; @@ -142,4 +143,29 @@ public final class NanoAppMessage implements Parcelable { return new NanoAppMessage[size]; } }; + + @Override + public String toString() { + int length = mMessageBody.length; + + String ret = "NanoAppMessage[type = " + mMessageType + ", length = " + mMessageBody.length + + " bytes, " + (mIsBroadcasted ? "broadcast" : "unicast") + ", nanoapp = 0x" + + Long.toHexString(mNanoAppId) + "]("; + if (length > 0) { + ret += "data = 0x"; + } + for (int i = 0; i < Math.min(length, DEBUG_LOG_NUM_BYTES); i++) { + ret += Byte.toHexString(mMessageBody[i], true /* upperCase */); + + if ((i + 1) % 4 == 0) { + ret += " "; + } + } + if (length > DEBUG_LOG_NUM_BYTES) { + ret += "..."; + } + ret += ")"; + + return ret; + } } diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 3556751f4af4..0cf7605c98a2 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -59,24 +59,56 @@ import java.util.stream.Stream; */ @SystemApi public final class ProgramSelector implements Parcelable { + /** Invalid program type. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_INVALID = 0; - /** Analogue AM radio (with or without RDS). */ + /** Analogue AM radio (with or without RDS). + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_AM = 1; - /** analogue FM radio (with or without RDS). */ + /** analogue FM radio (with or without RDS). + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_FM = 2; - /** AM HD Radio. */ + /** AM HD Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_AM_HD = 3; - /** FM HD Radio. */ + /** FM HD Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_FM_HD = 4; - /** Digital audio broadcasting. */ + /** Digital audio broadcasting. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_DAB = 5; - /** Digital Radio Mondiale. */ + /** Digital Radio Mondiale. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_DRMO = 6; - /** SiriusXM Satellite Radio. */ + /** SiriusXM Satellite Radio. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_SXM = 7; - /** Vendor-specific, not synced across devices. */ + /** Vendor-specific, not synced across devices. + * @deprecated use {@link ProgramIdentifier} instead + */ + @Deprecated public static final int PROGRAM_TYPE_VENDOR_START = 1000; + /** @deprecated use {@link ProgramIdentifier} instead */ + @Deprecated public static final int PROGRAM_TYPE_VENDOR_END = 1999; + /** @deprecated use {@link ProgramIdentifier} instead */ + @Deprecated @IntDef(prefix = { "PROGRAM_TYPE_" }, value = { PROGRAM_TYPE_INVALID, PROGRAM_TYPE_AM, @@ -112,18 +144,46 @@ public final class ProgramSelector implements Parcelable { * * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS), * as opposed to HD Radio standard (where it's 1-based). + * + * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead */ + @Deprecated public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; /** - * 24bit compound primary identifier for DAB. + * 64bit additional identifier for HD Radio. + * + * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not + * globally unique. To provide a best-effort solution, a short version of + * station name may be carried as additional identifier and may be used + * by the tuner hardware to double-check tuning. + * + * The name is limited to the first 8 A-Z0-9 characters (lowercase letters + * must be converted to uppercase). Encoded in little-endian ASCII: + * the first character of the name is the LSB. + * + * For example: "Abc" is encoded as 0x434241. + */ + public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; + /** + * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT} + */ + public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; + /** + * 28bit compound primary identifier for Digital Audio Broadcasting. * * Consists of (from the LSB): * - 16bit: SId; - * - 8bit: ECC code. + * - 8bit: ECC code; + * - 4bit: SCIdS. + * + * SCIdS (Service Component Identifier within the Service) value + * of 0 represents the main service, while 1 and above represents + * secondary services. + * * The remaining bits should be set to zeros when writing on the chip side * and ignored when read. */ - public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; + public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC; /** 16bit */ public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; /** 12bit */ @@ -134,7 +194,11 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; /** kHz */ public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; - /** 1: AM, 2:FM */ + /** + * 1: AM, 2:FM + * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead + */ + @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; /** 32bit */ public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; @@ -148,14 +212,29 @@ public final class ProgramSelector implements Parcelable { * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must * not be used in any program type other than 1015). */ - public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START; - public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END; + public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START; + /** + * @see {@link IDENTIFIER_TYPE_VENDOR_START} + */ + public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END; + /** + * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead + */ + @Deprecated + public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START; + /** + * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead + */ + @Deprecated + public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END; @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = { IDENTIFIER_TYPE_INVALID, IDENTIFIER_TYPE_AMFM_FREQUENCY, IDENTIFIER_TYPE_RDS_PI, IDENTIFIER_TYPE_HD_STATION_ID_EXT, IDENTIFIER_TYPE_HD_SUBCHANNEL, + IDENTIFIER_TYPE_HD_STATION_NAME, + IDENTIFIER_TYPE_DAB_SID_EXT, IDENTIFIER_TYPE_DAB_SIDECC, IDENTIFIER_TYPE_DAB_ENSEMBLE, IDENTIFIER_TYPE_DAB_SCID, @@ -166,7 +245,7 @@ public final class ProgramSelector implements Parcelable { IDENTIFIER_TYPE_SXM_SERVICE_ID, IDENTIFIER_TYPE_SXM_CHANNEL, }) - @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END) + @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END) @Retention(RetentionPolicy.SOURCE) public @interface IdentifierType {} @@ -205,7 +284,9 @@ public final class ProgramSelector implements Parcelable { * Type of a radio technology. * * @return program type. + * @deprecated use {@link getPrimaryId} instead */ + @Deprecated public @ProgramType int getProgramType() { return mProgramType; } @@ -273,7 +354,10 @@ public final class ProgramSelector implements Parcelable { * preserving elements order. * * @return an array of vendor identifiers, must not be modified. + * @deprecated for HAL 1.x compatibility; + * HAL 2.x uses standard primary/secondary lists for vendor IDs */ + @Deprecated public @NonNull long[] getVendorIds() { return mVendorIds; } @@ -427,6 +511,10 @@ public final class ProgramSelector implements Parcelable { private final long mValue; public Identifier(@IdentifierType int type, long value) { + if (type == IDENTIFIER_TYPE_HD_STATION_NAME) { + // see getType + type = IDENTIFIER_TYPE_HD_SUBCHANNEL; + } mType = type; mValue = value; } @@ -437,6 +525,13 @@ public final class ProgramSelector implements Parcelable { * @return type of an identifier. */ public @IdentifierType int getType() { + if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) { + /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ + * in possible values: sub channel is 0-7, station name is greater than ASCII space + * code (32). + */ + return IDENTIFIER_TYPE_HD_STATION_NAME; + } return mType; } diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 3fe531fd7960..eeb30e23d000 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -39,11 +39,11 @@ interface IIpSecService void closeUdpEncapsulationSocket(int resourceId); - IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder); + IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder); - void deleteTransportModeTransform(int transformId); + void deleteTransform(int transformId); void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId); - void removeTransportModeTransforms(in ParcelFileDescriptor socket, int transformId); + void removeTransportModeTransforms(in ParcelFileDescriptor socket); } diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl index 005dd6e16880..10667aecd128 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -18,10 +18,9 @@ package android.net; /** {@hide} */ oneway interface INetworkPolicyListener { - void onUidRulesChanged(int uid, int uidRules); void onMeteredIfacesChanged(in String[] meteredIfaces); void onRestrictBackgroundChanged(boolean restrictBackground); void onUidPoliciesChanged(int uid, int uidPolicies); - + void onSubscriptionOverride(int subId, int overrideMask, int overrideValue); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 7e37432fb06a..476e2f43649f 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -71,6 +71,7 @@ interface INetworkPolicyManager { SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage); void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage); String getSubscriptionPlansOwner(int subId); + void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage); void factoryReset(String subscriber); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 95e7f6031c72..90e3ffd550b4 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.DataUsageRequest; import android.net.INetworkStatsSession; +import android.net.Network; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; @@ -53,7 +54,7 @@ interface INetworkStatsService { void setUidForeground(int uid, boolean uidForeground); /** Force update of ifaces. */ - void forceUpdateIfaces(); + void forceUpdateIfaces(in Network[] defaultNetworks); /** Force update of statistics. */ void forceUpdate(); diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 2202df3baf92..f04f03f6b617 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; @@ -405,62 +406,56 @@ public final class IpSecManager { /** * Remove an IPsec transform from a stream socket. * - * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed - * regardless of the state of the transform. Removing a transform from a socket allows the - * socket to be reused for communication in the clear. + * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a + * socket allows the socket to be reused for communication in the clear. * * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling * {@link IpSecTransform#close()}, then communication on the socket will fail until this method * is called. * * @param socket a socket that previously had a transform applied to it - * @param transform the IPsec Transform that was previously applied to the given socket * @throws IOException indicating that the transform could not be removed from the socket */ - public void removeTransportModeTransforms(Socket socket, IpSecTransform transform) + public void removeTransportModeTransforms(Socket socket) throws IOException { - removeTransportModeTransforms(socket.getFileDescriptor$(), transform); + removeTransportModeTransforms(socket.getFileDescriptor$()); } /** * Remove an IPsec transform from a datagram socket. * - * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed - * regardless of the state of the transform. Removing a transform from a socket allows the - * socket to be reused for communication in the clear. + * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a + * socket allows the socket to be reused for communication in the clear. * * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling * {@link IpSecTransform#close()}, then communication on the socket will fail until this method * is called. * * @param socket a socket that previously had a transform applied to it - * @param transform the IPsec Transform that was previously applied to the given socket * @throws IOException indicating that the transform could not be removed from the socket */ - public void removeTransportModeTransforms(DatagramSocket socket, IpSecTransform transform) + public void removeTransportModeTransforms(DatagramSocket socket) throws IOException { - removeTransportModeTransforms(socket.getFileDescriptor$(), transform); + removeTransportModeTransforms(socket.getFileDescriptor$()); } /** * Remove an IPsec transform from a socket. * - * <p>Once removed, traffic on the socket will not be encrypted. This operation will succeed - * regardless of the state of the transform. Removing a transform from a socket allows the - * socket to be reused for communication in the clear. + * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a + * socket allows the socket to be reused for communication in the clear. * * <p>If an {@code IpSecTransform} object applied to this socket was deallocated by calling * {@link IpSecTransform#close()}, then communication on the socket will fail until this method * is called. * * @param socket a socket that previously had a transform applied to it - * @param transform the IPsec Transform that was previously applied to the given socket * @throws IOException indicating that the transform could not be removed from the socket */ - public void removeTransportModeTransforms(FileDescriptor socket, IpSecTransform transform) + public void removeTransportModeTransforms(FileDescriptor socket) throws IOException { try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { - mService.removeTransportModeTransforms(pfd, transform.getResourceId()); + mService.removeTransportModeTransforms(pfd); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -631,6 +626,133 @@ public final class IpSecManager { } /** + * This class represents an IpSecTunnelInterface + * + * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as + * local endpoints for IPsec tunnels. + * + * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be + * applied to provide IPsec security to packets sent through the tunnel. While a tunnel + * cannot be used in standalone mode within Android, the higher layers may use the tunnel + * to create Network objects which are accessible to the Android system. + * @hide + */ + @SystemApi + public static final class IpSecTunnelInterface implements AutoCloseable { + private final IIpSecService mService; + private final InetAddress mRemoteAddress; + private final InetAddress mLocalAddress; + private final Network mUnderlyingNetwork; + private final CloseGuard mCloseGuard = CloseGuard.get(); + private String mInterfaceName; + private int mResourceId = INVALID_RESOURCE_ID; + + /** Get the underlying SPI held by this object. */ + public String getInterfaceName() { + return mInterfaceName; + } + + /** + * Add an address to the IpSecTunnelInterface + * + * <p>Add an address which may be used as the local inner address for + * tunneled traffic. + * + * @param address the local address for traffic inside the tunnel + * @throws IOException if the address could not be added + * @hide + */ + public void addAddress(LinkAddress address) throws IOException { + } + + /** + * Remove an address from the IpSecTunnelInterface + * + * <p>Remove an address which was previously added to the IpSecTunnelInterface + * + * @param address to be removed + * @throws IOException if the address could not be removed + * @hide + */ + public void removeAddress(LinkAddress address) throws IOException { + } + + private IpSecTunnelInterface(@NonNull IIpSecService service, + @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, + @NonNull Network underlyingNetwork) + throws ResourceUnavailableException, IOException { + mService = service; + mLocalAddress = localAddress; + mRemoteAddress = remoteAddress; + mUnderlyingNetwork = underlyingNetwork; + // TODO: Call IpSecService + } + + /** + * Delete an IpSecTunnelInterface + * + * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system + * resources. Any packets bound for this interface either inbound or outbound will + * all be lost. + */ + @Override + public void close() { + // try { + // TODO: Call IpSecService + mResourceId = INVALID_RESOURCE_ID; + // } catch (RemoteException e) { + // throw e.rethrowFromSystemServer(); + // } + mCloseGuard.close(); + } + + /** Check that the Interface was closed properly. */ + @Override + protected void finalize() throws Throwable { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + } + + /** + * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic. + * + * @param localAddress The local addres of the tunnel + * @param remoteAddress The local addres of the tunnel + * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. + * This network should almost certainly be a network such as WiFi with an L2 address. + * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties + * @throws IOException indicating that the socket could not be opened or bound + * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open + * @hide + */ + @SystemApi + public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, + @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) + throws ResourceUnavailableException, IOException { + return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork); + } + + /** + * Apply a transform to the IpSecTunnelInterface + * + * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied + * transform. + * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which + * the transform will be used. + * @param transform an {@link IpSecTransform} created in tunnel mode + * @throws IOException indicating that the transform could not be applied due to a lower + * layer failure. + * @hide + */ + @SystemApi + void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction, + IpSecTransform transform) throws IOException { + // TODO: call IpSecService + } + /** * Construct an instance of IpSecManager within an application context. * * @param context the application context for this manager diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 7b9b4830929d..37e2c4fbf04a 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -124,8 +124,7 @@ public final class IpSecTransform implements AutoCloseable { synchronized (this) { try { IIpSecService svc = getIpSecService(); - IpSecTransformResponse result = - svc.createTransportModeTransform(mConfig, new Binder()); + IpSecTransformResponse result = svc.createTransform(mConfig, new Binder()); int status = result.status; checkResultStatus(status); mResourceId = result.resourceId; @@ -170,7 +169,7 @@ public final class IpSecTransform implements AutoCloseable { * still want to clear out the transform. */ IIpSecService svc = getIpSecService(); - svc.deleteTransportModeTransform(mResourceId); + svc.deleteTransform(mResourceId); stopKeepalive(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -300,21 +299,6 @@ public final class IpSecTransform implements AutoCloseable { } /** - * Set the {@link Network} which will carry tunneled traffic. - * - * <p>Restricts the transformed traffic to a particular {@link Network}. This is required - * for tunnel mode, otherwise tunneled traffic would be sent on the default network. - * - * @hide - */ - @SystemApi - public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) { - Preconditions.checkNotNull(net); - mConfig.setNetwork(net); - return this; - } - - /** * Add UDP encapsulation to an IPv4 transform. * * <p>This allows IPsec traffic to pass through a NAT. @@ -415,6 +399,7 @@ public final class IpSecTransform implements AutoCloseable { * @throws IOException indicating other errors * @hide */ + @SystemApi public IpSecTransform buildTunnelModeTransform( @NonNull InetAddress sourceAddress, @NonNull IpSecManager.SecurityParameterIndex spi) diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 4e474c8e478c..f525b1f37518 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -50,6 +50,8 @@ public final class LinkProperties implements Parcelable { private String mIfaceName; private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>(); + private boolean mUsePrivateDns; + private String mPrivateDnsServerName; private String mDomains; private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); private ProxyInfo mHttpProxy; @@ -165,6 +167,8 @@ public final class LinkProperties implements Parcelable { mIfaceName = source.getInterfaceName(); for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l); for (InetAddress i : source.getDnsServers()) mDnses.add(i); + mUsePrivateDns = source.mUsePrivateDns; + mPrivateDnsServerName = source.mPrivateDnsServerName; mDomains = source.getDomains(); for (RouteInfo r : source.getRoutes()) mRoutes.add(r); mHttpProxy = (source.getHttpProxy() == null) ? @@ -391,6 +395,59 @@ public final class LinkProperties implements Parcelable { } /** + * Set whether private DNS is currently in use on this network. + * + * @param usePrivateDns The private DNS state. + * @hide + */ + public void setUsePrivateDns(boolean usePrivateDns) { + mUsePrivateDns = usePrivateDns; + } + + /** + * Returns whether private DNS is currently in use on this network. When + * private DNS is in use, applications must not send unencrypted DNS + * queries as doing so could reveal private user information. Furthermore, + * if private DNS is in use and {@link #getPrivateDnsServerName} is not + * {@code null}, DNS queries must be sent to the specified DNS server. + * + * @return {@code true} if private DNS is in use, {@code false} otherwise. + */ + public boolean isPrivateDnsActive() { + return mUsePrivateDns; + } + + /** + * Set the name of the private DNS server to which private DNS queries + * should be sent when in strict mode. This value should be {@code null} + * when private DNS is off or in opportunistic mode. + * + * @param privateDnsServerName The private DNS server name. + * @hide + */ + public void setPrivateDnsServerName(@Nullable String privateDnsServerName) { + mPrivateDnsServerName = privateDnsServerName; + } + + /** + * Returns the private DNS server name that is in use. If not {@code null}, + * private DNS is in strict mode. In this mode, applications should ensure + * that all DNS queries are encrypted and sent to this hostname and that + * queries are only sent if the hostname's certificate is valid. If + * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private + * DNS is in opportunistic mode, and applications should ensure that DNS + * queries are encrypted and sent to a DNS server returned by + * {@link #getDnsServers}. System DNS will handle each of these cases + * correctly, but applications implementing their own DNS lookups must make + * sure to follow these requirements. + * + * @return The private DNS server name. + */ + public @Nullable String getPrivateDnsServerName() { + return mPrivateDnsServerName; + } + + /** * Sets the DNS domain search path used on this link. * * @param domains A {@link String} listing in priority order the comma separated @@ -622,6 +679,8 @@ public final class LinkProperties implements Parcelable { mIfaceName = null; mLinkAddresses.clear(); mDnses.clear(); + mUsePrivateDns = false; + mPrivateDnsServerName = null; mDomains = null; mRoutes.clear(); mHttpProxy = null; @@ -649,6 +708,13 @@ public final class LinkProperties implements Parcelable { for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; dns += "] "; + String usePrivateDns = "UsePrivateDns: " + mUsePrivateDns + " "; + + String privateDnsServerName = ""; + if (privateDnsServerName != null) { + privateDnsServerName = "PrivateDnsServerName: " + mPrivateDnsServerName + " "; + } + String domainName = "Domains: " + mDomains; String mtu = " MTU: " + mMtu; @@ -671,8 +737,9 @@ public final class LinkProperties implements Parcelable { } stacked += "] "; } - return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu - + tcpBuffSizes + proxy + stacked + "}"; + return "{" + ifaceName + linkAddresses + routes + dns + usePrivateDns + + privateDnsServerName + domainName + mtu + tcpBuffSizes + proxy + + stacked + "}"; } /** @@ -896,6 +963,20 @@ public final class LinkProperties implements Parcelable { } /** + * Compares this {@code LinkProperties} private DNS settings against the + * target. + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalPrivateDns(LinkProperties target) { + return (isPrivateDnsActive() == target.isPrivateDnsActive() + && TextUtils.equals(getPrivateDnsServerName(), + target.getPrivateDnsServerName())); + } + + /** * Compares this {@code LinkProperties} Routes against the target * * @param target LinkProperties to compare. @@ -989,14 +1070,15 @@ public final class LinkProperties implements Parcelable { * stacked interfaces are not so much a property of the link as a * description of connections between links. */ - return isIdenticalInterfaceName(target) && - isIdenticalAddresses(target) && - isIdenticalDnses(target) && - isIdenticalRoutes(target) && - isIdenticalHttpProxy(target) && - isIdenticalStackedLinks(target) && - isIdenticalMtu(target) && - isIdenticalTcpBufferSizes(target); + return isIdenticalInterfaceName(target) + && isIdenticalAddresses(target) + && isIdenticalDnses(target) + && isIdenticalPrivateDns(target) + && isIdenticalRoutes(target) + && isIdenticalHttpProxy(target) + && isIdenticalStackedLinks(target) + && isIdenticalMtu(target) + && isIdenticalTcpBufferSizes(target); } /** @@ -1091,7 +1173,9 @@ public final class LinkProperties implements Parcelable { + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()) + mStackedLinks.hashCode() * 47) + mMtu * 51 - + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()); + + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()) + + (mUsePrivateDns ? 57 : 0) + + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()); } /** @@ -1108,6 +1192,8 @@ public final class LinkProperties implements Parcelable { for(InetAddress d : mDnses) { dest.writeByteArray(d.getAddress()); } + dest.writeBoolean(mUsePrivateDns); + dest.writeString(mPrivateDnsServerName); dest.writeString(mDomains); dest.writeInt(mMtu); dest.writeString(mTcpBufferSizes); @@ -1148,6 +1234,8 @@ public final class LinkProperties implements Parcelable { netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); } catch (UnknownHostException e) { } } + netProp.setUsePrivateDns(in.readBoolean()); + netProp.setPrivateDnsServerName(in.readString()); netProp.setDomains(in.readString()); netProp.setMtu(in.readInt()); netProp.setTcpBufferSizes(in.readString()); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 8b03fa81d483..214ff64c6315 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -108,6 +108,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_CAPTIVE_PORTAL, NET_CAPABILITY_NOT_ROAMING, NET_CAPABILITY_FOREGROUND, + NET_CAPABILITY_NOT_CONGESTED, }) public @interface NetCapability { } @@ -235,8 +236,17 @@ public final class NetworkCapabilities implements Parcelable { */ public static final int NET_CAPABILITY_FOREGROUND = 19; + /** + * Indicates that this network is not congested. + * <p> + * When a network is congested, the device should defer network traffic that + * can be done at a later time without breaking developer contracts. + * @hide + */ + public static final int NET_CAPABILITY_NOT_CONGESTED = 20; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -249,7 +259,8 @@ public final class NetworkCapabilities implements Parcelable { (1 << NET_CAPABILITY_VALIDATED) | (1 << NET_CAPABILITY_CAPTIVE_PORTAL) | (1 << NET_CAPABILITY_NOT_ROAMING) | - (1 << NET_CAPABILITY_FOREGROUND); + (1 << NET_CAPABILITY_FOREGROUND) | + (1 << NET_CAPABILITY_NOT_CONGESTED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -387,12 +398,9 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public String describeFirstNonRequestableCapability() { - if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED"; - if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL"; - if (hasCapability(NET_CAPABILITY_FOREGROUND)) return "NET_CAPABILITY_FOREGROUND"; - // This cannot happen unless the preceding checks are incomplete. - if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) { - return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities); + final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES); + if (nonRequestable != 0) { + return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]); } if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth"; if (hasSignalStrength()) return "signalStrength"; @@ -1080,6 +1088,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL"; case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING"; case NET_CAPABILITY_FOREGROUND: return "FOREGROUND"; + case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED"; default: return Integer.toString(capability); } } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index d3b35998be13..ce2de8554dcc 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -58,21 +58,24 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { final String mNetworkId; final boolean mRoaming; final boolean mMetered; + final boolean mDefaultNetwork; public NetworkIdentity( int type, int subType, String subscriberId, String networkId, boolean roaming, - boolean metered) { + boolean metered, boolean defaultNetwork) { mType = type; mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType; mSubscriberId = subscriberId; mNetworkId = networkId; mRoaming = roaming; mMetered = metered; + mDefaultNetwork = defaultNetwork; } @Override public int hashCode() { - return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered); + return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered, + mDefaultNetwork); } @Override @@ -82,7 +85,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming && Objects.equals(mSubscriberId, ident.mSubscriberId) && Objects.equals(mNetworkId, ident.mNetworkId) - && mMetered == ident.mMetered; + && mMetered == ident.mMetered + && mDefaultNetwork == ident.mDefaultNetwork; } return false; } @@ -109,6 +113,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { builder.append(", ROAMING"); } builder.append(", metered=").append(mMetered); + builder.append(", defaultNetwork=").append(mDefaultNetwork); return builder.append("}").toString(); } @@ -125,6 +130,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId); proto.write(NetworkIdentityProto.ROAMING, mRoaming); proto.write(NetworkIdentityProto.METERED, mMetered); + proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork); proto.end(start); } @@ -153,6 +159,10 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { return mMetered; } + public boolean getDefaultNetwork() { + return mDefaultNetwork; + } + /** * Scrub given IMSI on production builds. */ @@ -183,7 +193,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { * Build a {@link NetworkIdentity} from the given {@link NetworkState}, * assuming that any mobile networks are using the current IMSI. */ - public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) { + public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state, + boolean defaultNetwork) { final int type = state.networkInfo.getType(); final int subType = state.networkInfo.getSubtype(); @@ -216,7 +227,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } } - return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered); + return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered, + defaultNetwork); } @Override @@ -237,6 +249,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { if (res == 0) { res = Boolean.compare(mMetered, another.mMetered); } + if (res == 0) { + res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork); + } return res; } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 9ef26a9f5a5b..2c5a021ec42a 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -113,6 +113,9 @@ public class NetworkPolicyManager { */ public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE"; + public static final int OVERRIDE_UNMETERED = 1 << 0; + public static final int OVERRIDE_CONGESTED = 1 << 1; + private final Context mContext; private INetworkPolicyManager mService; @@ -347,4 +350,13 @@ public class NetworkPolicyManager { public static String resolveNetworkId(String ssid) { return WifiInfo.removeDoubleQuotes(ssid); } + + /** {@hide} */ + public static class Listener extends INetworkPolicyListener.Stub { + @Override public void onUidRulesChanged(int uid, int uidRules) { } + @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { } + @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { } + @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { } + @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue) { } + } } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 171adc054bbe..a85f80e38dfd 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -82,6 +82,13 @@ public class NetworkStats implements Parcelable { /** {@link #roaming} value where roaming data is accounted. */ public static final int ROAMING_YES = 1; + /** {@link #onDefaultNetwork} value to account for all default network states. */ + public static final int DEFAULT_NETWORK_ALL = -1; + /** {@link #onDefaultNetwork} value to account for usage while not the default network. */ + public static final int DEFAULT_NETWORK_NO = 0; + /** {@link #onDefaultNetwork} value to account for usage while the default network. */ + public static final int DEFAULT_NETWORK_YES = 1; + /** Denotes a request for stats at the interface level. */ public static final int STATS_PER_IFACE = 0; /** Denotes a request for stats at the interface and UID level. */ @@ -102,6 +109,7 @@ public class NetworkStats implements Parcelable { private int[] tag; private int[] metered; private int[] roaming; + private int[] defaultNetwork; private long[] rxBytes; private long[] rxPackets; private long[] txBytes; @@ -125,6 +133,12 @@ public class NetworkStats implements Parcelable { * getSummary(). */ public int roaming; + /** + * Note that this is only populated w/ the default value when read from /proc or written + * to disk. We merge in the correct value when reporting this value to clients of + * getSummary(). + */ + public int defaultNetwork; public long rxBytes; public long rxPackets; public long txBytes; @@ -142,18 +156,27 @@ public class NetworkStats implements Parcelable { public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { - this(iface, uid, set, tag, METERED_NO, ROAMING_NO, rxBytes, rxPackets, txBytes, - txPackets, operations); + this(iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, + rxBytes, rxPackets, txBytes, txPackets, operations); } + // TODO: fix the the telephony code to pass DEFAULT_NETWORK_YES and remove this constructor. public Entry(String iface, int uid, int set, int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { + this(iface, uid, set, tag, metered, roaming, DEFAULT_NETWORK_YES, rxBytes, rxPackets, + txBytes, txPackets, operations); + } + + public Entry(String iface, int uid, int set, int tag, int metered, int roaming, + int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, + long operations) { this.iface = iface; this.uid = uid; this.set = set; this.tag = tag; this.metered = metered; this.roaming = roaming; + this.defaultNetwork = defaultNetwork; this.rxBytes = rxBytes; this.rxPackets = rxPackets; this.txBytes = txBytes; @@ -187,6 +210,7 @@ public class NetworkStats implements Parcelable { builder.append(" tag=").append(tagToString(tag)); builder.append(" metered=").append(meteredToString(metered)); builder.append(" roaming=").append(roamingToString(roaming)); + builder.append(" defaultNetwork=").append(defaultNetworkToString(defaultNetwork)); builder.append(" rxBytes=").append(rxBytes); builder.append(" rxPackets=").append(rxPackets); builder.append(" txBytes=").append(txBytes); @@ -200,7 +224,8 @@ public class NetworkStats implements Parcelable { if (o instanceof Entry) { final Entry e = (Entry) o; return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered - && roaming == e.roaming && rxBytes == e.rxBytes && rxPackets == e.rxPackets + && roaming == e.roaming && defaultNetwork == e.defaultNetwork + && rxBytes == e.rxBytes && rxPackets == e.rxPackets && txBytes == e.txBytes && txPackets == e.txPackets && operations == e.operations && iface.equals(e.iface); } @@ -209,7 +234,7 @@ public class NetworkStats implements Parcelable { @Override public int hashCode() { - return Objects.hash(uid, set, tag, metered, roaming, iface); + return Objects.hash(uid, set, tag, metered, roaming, defaultNetwork, iface); } } @@ -224,6 +249,7 @@ public class NetworkStats implements Parcelable { this.tag = new int[initialSize]; this.metered = new int[initialSize]; this.roaming = new int[initialSize]; + this.defaultNetwork = new int[initialSize]; this.rxBytes = new long[initialSize]; this.rxPackets = new long[initialSize]; this.txBytes = new long[initialSize]; @@ -238,6 +264,7 @@ public class NetworkStats implements Parcelable { this.tag = EmptyArray.INT; this.metered = EmptyArray.INT; this.roaming = EmptyArray.INT; + this.defaultNetwork = EmptyArray.INT; this.rxBytes = EmptyArray.LONG; this.rxPackets = EmptyArray.LONG; this.txBytes = EmptyArray.LONG; @@ -256,6 +283,7 @@ public class NetworkStats implements Parcelable { tag = parcel.createIntArray(); metered = parcel.createIntArray(); roaming = parcel.createIntArray(); + defaultNetwork = parcel.createIntArray(); rxBytes = parcel.createLongArray(); rxPackets = parcel.createLongArray(); txBytes = parcel.createLongArray(); @@ -274,6 +302,7 @@ public class NetworkStats implements Parcelable { dest.writeIntArray(tag); dest.writeIntArray(metered); dest.writeIntArray(roaming); + dest.writeIntArray(defaultNetwork); dest.writeLongArray(rxBytes); dest.writeLongArray(rxPackets); dest.writeLongArray(txBytes); @@ -308,10 +337,11 @@ public class NetworkStats implements Parcelable { @VisibleForTesting public NetworkStats addValues(String iface, int uid, int set, int tag, int metered, int roaming, - long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { + int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets, + long operations) { return addValues(new Entry( - iface, uid, set, tag, metered, roaming, rxBytes, rxPackets, txBytes, txPackets, - operations)); + iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets, + txBytes, txPackets, operations)); } /** @@ -327,6 +357,7 @@ public class NetworkStats implements Parcelable { tag = Arrays.copyOf(tag, newLength); metered = Arrays.copyOf(metered, newLength); roaming = Arrays.copyOf(roaming, newLength); + defaultNetwork = Arrays.copyOf(defaultNetwork, newLength); rxBytes = Arrays.copyOf(rxBytes, newLength); rxPackets = Arrays.copyOf(rxPackets, newLength); txBytes = Arrays.copyOf(txBytes, newLength); @@ -341,6 +372,7 @@ public class NetworkStats implements Parcelable { tag[size] = entry.tag; metered[size] = entry.metered; roaming[size] = entry.roaming; + defaultNetwork[size] = entry.defaultNetwork; rxBytes[size] = entry.rxBytes; rxPackets[size] = entry.rxPackets; txBytes[size] = entry.txBytes; @@ -362,6 +394,7 @@ public class NetworkStats implements Parcelable { entry.tag = tag[i]; entry.metered = metered[i]; entry.roaming = roaming[i]; + entry.defaultNetwork = defaultNetwork[i]; entry.rxBytes = rxBytes[i]; entry.rxPackets = rxPackets[i]; entry.txBytes = txBytes[i]; @@ -416,7 +449,7 @@ public class NetworkStats implements Parcelable { */ public NetworkStats combineValues(Entry entry) { final int i = findIndex(entry.iface, entry.uid, entry.set, entry.tag, entry.metered, - entry.roaming); + entry.roaming, entry.defaultNetwork); if (i == -1) { // only create new entry when positive contribution addValues(entry); @@ -444,10 +477,12 @@ public class NetworkStats implements Parcelable { /** * Find first stats index that matches the requested parameters. */ - public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming) { + public int findIndex(String iface, int uid, int set, int tag, int metered, int roaming, + int defaultNetwork) { for (int i = 0; i < size; i++) { if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] && metered == this.metered[i] && roaming == this.roaming[i] + && defaultNetwork == this.defaultNetwork[i] && Objects.equals(iface, this.iface[i])) { return i; } @@ -461,7 +496,7 @@ public class NetworkStats implements Parcelable { */ @VisibleForTesting public int findIndexHinted(String iface, int uid, int set, int tag, int metered, int roaming, - int hintIndex) { + int defaultNetwork, int hintIndex) { for (int offset = 0; offset < size; offset++) { final int halfOffset = offset / 2; @@ -475,6 +510,7 @@ public class NetworkStats implements Parcelable { if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i] && metered == this.metered[i] && roaming == this.roaming[i] + && defaultNetwork == this.defaultNetwork[i] && Objects.equals(iface, this.iface[i])) { return i; } @@ -489,7 +525,8 @@ public class NetworkStats implements Parcelable { */ public void spliceOperationsFrom(NetworkStats stats) { for (int i = 0; i < size; i++) { - final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i]); + final int j = stats.findIndex(iface[i], uid[i], set[i], tag[i], metered[i], roaming[i], + defaultNetwork[i]); if (j == -1) { operations[i] = 0; } else { @@ -581,6 +618,7 @@ public class NetworkStats implements Parcelable { entry.tag = TAG_NONE; entry.metered = METERED_ALL; entry.roaming = ROAMING_ALL; + entry.defaultNetwork = DEFAULT_NETWORK_ALL; entry.rxBytes = 0; entry.rxPackets = 0; entry.txBytes = 0; @@ -677,6 +715,7 @@ public class NetworkStats implements Parcelable { entry.tag = left.tag[i]; entry.metered = left.metered[i]; entry.roaming = left.roaming[i]; + entry.defaultNetwork = left.defaultNetwork[i]; entry.rxBytes = left.rxBytes[i]; entry.rxPackets = left.rxPackets[i]; entry.txBytes = left.txBytes[i]; @@ -685,7 +724,7 @@ public class NetworkStats implements Parcelable { // find remote row that matches, and subtract final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, - entry.metered, entry.roaming, i); + entry.metered, entry.roaming, entry.defaultNetwork, i); if (j != -1) { // Found matching row, subtract remote value. entry.rxBytes -= right.rxBytes[j]; @@ -725,6 +764,7 @@ public class NetworkStats implements Parcelable { entry.tag = TAG_NONE; entry.metered = METERED_ALL; entry.roaming = ROAMING_ALL; + entry.defaultNetwork = DEFAULT_NETWORK_ALL; entry.operations = 0L; for (int i = 0; i < size; i++) { @@ -755,6 +795,7 @@ public class NetworkStats implements Parcelable { entry.tag = TAG_NONE; entry.metered = METERED_ALL; entry.roaming = ROAMING_ALL; + entry.defaultNetwork = DEFAULT_NETWORK_ALL; for (int i = 0; i < size; i++) { // skip specific tags, since already counted in TAG_NONE @@ -802,6 +843,7 @@ public class NetworkStats implements Parcelable { pw.print(" tag="); pw.print(tagToString(tag[i])); pw.print(" metered="); pw.print(meteredToString(metered[i])); pw.print(" roaming="); pw.print(roamingToString(roaming[i])); + pw.print(" defaultNetwork="); pw.print(defaultNetworkToString(defaultNetwork[i])); pw.print(" rxBytes="); pw.print(rxBytes[i]); pw.print(" rxPackets="); pw.print(rxPackets[i]); pw.print(" txBytes="); pw.print(txBytes[i]); @@ -900,6 +942,22 @@ public class NetworkStats implements Parcelable { } } + /** + * Return text description of {@link #defaultNetwork} value. + */ + public static String defaultNetworkToString(int defaultNetwork) { + switch (defaultNetwork) { + case DEFAULT_NETWORK_ALL: + return "ALL"; + case DEFAULT_NETWORK_NO: + return "NO"; + case DEFAULT_NETWORK_YES: + return "YES"; + default: + return "UNKNOWN"; + } + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); @@ -1055,6 +1113,7 @@ public class NetworkStats implements Parcelable { tmpEntry.set = set[i]; tmpEntry.metered = metered[i]; tmpEntry.roaming = roaming[i]; + tmpEntry.defaultNetwork = defaultNetwork[i]; combineValues(tmpEntry); if (tag[i] == TAG_NONE) { moved.add(tmpEntry); @@ -1075,6 +1134,7 @@ public class NetworkStats implements Parcelable { moved.iface = underlyingIface; moved.metered = METERED_ALL; moved.roaming = ROAMING_ALL; + moved.defaultNetwork = DEFAULT_NETWORK_ALL; combineValues(moved); // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than @@ -1085,13 +1145,13 @@ public class NetworkStats implements Parcelable { // roaming data after applying these adjustments, by checking the NetworkIdentity of the // underlying iface. int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); if (idxVpnBackground != -1) { tunSubtract(idxVpnBackground, this, moved); } int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); if (idxVpnForeground != -1) { tunSubtract(idxVpnForeground, this, moved); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dc271d8639d5..49879a8a84a7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -35,6 +35,7 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -683,6 +684,14 @@ public abstract class BatteryStats implements Parcelable { public abstract long[] getCpuFreqTimes(int which); public abstract long[] getScreenOffCpuFreqTimes(int which); + /** + * Returns cpu active time of an uid. + */ + public abstract long getCpuActiveTime(); + /** + * Returns cpu times of an uid on each cluster + */ + public abstract long[] getCpuClusterTimes(); /** * Returns cpu times of an uid at a particular process state. @@ -1500,6 +1509,10 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4; public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK = 0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT; + // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS + public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7; + public static final int STATE2_GPS_SIGNAL_QUALITY_MASK = + 0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT; public static final int STATE2_POWER_SAVE_FLAG = 1<<31; public static final int STATE2_VIDEO_ON_FLAG = 1<<30; @@ -2088,6 +2101,23 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getNumConnectivityChange(int which); + + /** + * Returns the time in microseconds that the phone has been running with + * the given GPS signal quality level + * + * {@hide} + */ + public abstract long getGpsSignalQualityTime(int strengthBin, + long elapsedRealtimeUs, int which); + + /** + * Returns the GPS battery drain in mA-ms + * + * {@hide} + */ + public abstract long getGpsBatteryDrainMaMs(); + /** * Returns the time in microseconds that the phone has been on while the device was * running on battery. @@ -2312,6 +2342,9 @@ public abstract class BatteryStats implements Parcelable { WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES), new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"), new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"), + new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss", + new String[] { "poor", "good"}, new String[] { "poor", "good"}), }; public static final String[] HISTORY_EVENT_NAMES = new String[] { @@ -4732,6 +4765,43 @@ public abstract class BatteryStats implements Parcelable { pw.print(prefix); sb.setLength(0); sb.append(prefix); + sb.append(" GPS Statistics:"); + pw.println(sb.toString()); + + sb.setLength(0); + sb.append(prefix); + sb.append(" GPS signal quality (Top 4 Average CN0):"); + final String[] gpsSignalQualityDescription = new String[]{ + "poor (less than 20 dBHz): ", + "good (greater than 20 dBHz): "}; + final int numGpsSignalQualityBins = Math.min(GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS, + gpsSignalQualityDescription.length); + for (int i=0; i<numGpsSignalQualityBins; i++) { + final long time = getGpsSignalQualityTime(i, rawRealtime, which); + sb.append("\n "); + sb.append(prefix); + sb.append(" "); + sb.append(gpsSignalQualityDescription[i]); + formatTimeMs(sb, time/1000); + sb.append("("); + sb.append(formatRatioLocked(time, whichBatteryRealtime)); + sb.append(") "); + } + pw.println(sb.toString()); + + final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs(); + if (gpsBatteryDrainMaMs > 0) { + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); + sb.append(" Battery Drain (mAh): "); + sb.append(Double.toString(((double) gpsBatteryDrainMaMs)/(3600 * 1000))); + pw.println(sb.toString()); + } + + pw.print(prefix); + sb.setLength(0); + sb.append(prefix); sb.append(" CONNECTIVITY POWER SUMMARY END"); pw.println(sb.toString()); pw.println(""); diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl index 5e8590af11f1..3de953a2dfbe 100644 --- a/core/java/android/os/IPermissionController.aidl +++ b/core/java/android/os/IPermissionController.aidl @@ -22,4 +22,5 @@ interface IPermissionController { boolean checkPermission(String permission, int pid, int uid); String[] getPackagesForUid(int uid); boolean isRuntimePermission(String permission); + int getPackageUid(String packageName, int flags); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 0874d93e8262..7654e9b6ee22 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -151,6 +151,12 @@ public class Process { */ public static final int OTA_UPDATE_UID = 1061; + /** + * Defines the UID used for incidentd. + * @hide + */ + public static final int INCIDENTD_UID = 1067; + /** {@hide} */ public static final int NOBODY_UID = 9999; @@ -269,6 +275,15 @@ public class Process { public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; /** + * Standard priority of video threads. Applications can not normally + * change to this priority. + * Use with {@link #setThreadPriority(int)} and + * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal + * {@link java.lang.Thread} class. + */ + public static final int THREAD_PRIORITY_VIDEO = -10; + + /** * Standard priority of audio threads. Applications can not normally * change to this priority. * Use with {@link #setThreadPriority(int)} and diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 673a8ba661b6..57db9d19f42d 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -61,6 +61,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -101,6 +102,9 @@ public class RecoverySystem { private static final String ACTION_EUICC_FACTORY_RESET = "com.android.internal.action.EUICC_FACTORY_RESET"; + /** used in {@link #wipeEuiccData} as package name of callback intent */ + private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android"; + /** * The recovery image uses this file to identify the location (i.e. blocks) * of an OTA package on the /data partition. The block map file is @@ -751,7 +755,7 @@ public class RecoverySystem { // Block until the ordered broadcast has completed. condition.block(); - wipeEuiccData(context, wipeEuicc); + wipeEuiccData(context, wipeEuicc, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK); String shutdownArg = null; if (shutdown) { @@ -767,19 +771,29 @@ public class RecoverySystem { bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); } - private static void wipeEuiccData(Context context, final boolean isWipeEuicc) { + /** + * Returns whether wipe Euicc data successfully or not. + * + * @param isWipeEuicc whether we want to wipe Euicc data or not + * @param packageName the package name of the caller app. + * + * @hide + */ + public static boolean wipeEuiccData( + Context context, final boolean isWipeEuicc, final String packageName) { ContentResolver cr = context.getContentResolver(); if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles, // as there's nothing to wipe nor retain. Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned"); - return; + return true; } EuiccManager euiccManager = (EuiccManager) context.getSystemService( Context.EUICC_SERVICE); if (euiccManager != null && euiccManager.isEnabled()) { CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1); + final AtomicBoolean wipingSucceeded = new AtomicBoolean(false); BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() { @Override @@ -801,6 +815,7 @@ public class RecoverySystem { } else { Log.d(TAG, "Successfully retained euicc data."); } + wipingSucceeded.set(true /* newValue */); } euiccFactoryResetLatch.countDown(); } @@ -808,7 +823,7 @@ public class RecoverySystem { }; Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET); - intent.setPackage("android"); + intent.setPackage(packageName); PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM); IntentFilter filterConsent = new IntentFilter(); @@ -839,8 +854,8 @@ public class RecoverySystem { } else { Log.e(TAG, "Timeout retaining eUICC data."); } + return false; } - context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver); } catch (InterruptedException e) { Thread.currentThread().interrupt(); if (isWipeEuicc) { @@ -848,8 +863,13 @@ public class RecoverySystem { } else { Log.e(TAG, "Retaining eUICC data interrupted", e); } + return false; + } finally { + context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver); } + return wipingSucceeded.get(); } + return false; } /** {@hide} */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7b0c153f5234..13b5b5c90c2c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -791,6 +791,7 @@ public class UserManager { * @see #getUserRestrictions() * @hide */ + @SystemApi public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; /** diff --git a/core/java/android/os/connectivity/GpsBatteryStats.aidl b/core/java/android/os/connectivity/GpsBatteryStats.aidl new file mode 100644 index 000000000000..7b96d1a8e062 --- /dev/null +++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +package android.os.connectivity; + +/** {@hide} */ +parcelable GpsBatteryStats;
\ No newline at end of file diff --git a/core/java/android/os/connectivity/GpsBatteryStats.java b/core/java/android/os/connectivity/GpsBatteryStats.java new file mode 100644 index 000000000000..f2ac5ef6d40b --- /dev/null +++ b/core/java/android/os/connectivity/GpsBatteryStats.java @@ -0,0 +1,108 @@ +/* + * 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. + */ +package android.os.connectivity; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.location.gnssmetrics.GnssMetrics; + +import java.util.Arrays; + +/** + * API for GPS power stats + * + * @hide + */ +public final class GpsBatteryStats implements Parcelable { + + private long mLoggingDurationMs; + private long mEnergyConsumedMaMs; + private long[] mTimeInGpsSignalQualityLevel; + + public static final Parcelable.Creator<GpsBatteryStats> CREATOR = new + Parcelable.Creator<GpsBatteryStats>() { + public GpsBatteryStats createFromParcel(Parcel in) { + return new GpsBatteryStats(in); + } + + public GpsBatteryStats[] newArray(int size) { + return new GpsBatteryStats[size]; + } + }; + + public GpsBatteryStats() { + initialize(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mLoggingDurationMs); + out.writeLong(mEnergyConsumedMaMs); + out.writeLongArray(mTimeInGpsSignalQualityLevel); + } + + public void readFromParcel(Parcel in) { + mLoggingDurationMs = in.readLong(); + mEnergyConsumedMaMs = in.readLong(); + in.readLongArray(mTimeInGpsSignalQualityLevel); + } + + public long getLoggingDurationMs() { + return mLoggingDurationMs; + } + + public long getEnergyConsumedMaMs() { + return mEnergyConsumedMaMs; + } + + public long[] getTimeInGpsSignalQualityLevel() { + return mTimeInGpsSignalQualityLevel; + } + + public void setLoggingDurationMs(long t) { + mLoggingDurationMs = t; + return; + } + + public void setEnergyConsumedMaMs(long e) { + mEnergyConsumedMaMs = e; + return; + } + + public void setTimeInGpsSignalQualityLevel(long[] t) { + mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0, + Math.min(t.length, GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS)); + return; + } + + @Override + public int describeContents() { + return 0; + } + + private GpsBatteryStats(Parcel in) { + initialize(); + readFromParcel(in); + } + + private void initialize() { + mLoggingDurationMs = 0; + mEnergyConsumedMaMs = 0; + mTimeInGpsSignalQualityLevel = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + return; + } +}
\ No newline at end of file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 456ea6a7a62d..60ce42b8a76e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -16,6 +16,16 @@ package android.provider; +import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR; +import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR; +import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR; +import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR; +import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR; +import static android.provider.SettingsValidators.LOCALE_VALIDATOR; +import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; +import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR; +import static android.provider.SettingsValidators.URI_VALIDATOR; + import android.Manifest; import android.annotation.IntDef; import android.annotation.IntRange; @@ -65,6 +75,8 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.SettingsValidators; +import android.provider.SettingsValidators.Validator; import android.speech.tts.TextToSpeech; import android.telephony.SubscriptionManager; import android.text.TextUtils; @@ -76,7 +88,6 @@ import android.util.MemoryIntArray; import android.util.StatsLog; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ILockSettings; import java.io.IOException; @@ -2119,11 +2130,6 @@ public final class Settings { private static final float DEFAULT_FONT_SCALE = 1.0f; - /** @hide */ - public static interface Validator { - public boolean validate(String value); - } - /** * The content:// style URL for this table */ @@ -2228,41 +2234,6 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL); } - private static final Validator sBooleanValidator = - new DiscreteValueValidator(new String[] {"0", "1"}); - - private static final Validator sNonNegativeIntegerValidator = new Validator() { - @Override - public boolean validate(String value) { - try { - return Integer.parseInt(value) >= 0; - } catch (NumberFormatException e) { - return false; - } - } - }; - - private static final Validator sUriValidator = new Validator() { - @Override - public boolean validate(String value) { - try { - Uri.decode(value); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } - }; - - private static final Validator sLenientIpAddressValidator = new Validator() { - private static final int MAX_IPV6_LENGTH = 45; - - @Override - public boolean validate(String value) { - return value.length() <= MAX_IPV6_LENGTH; - } - }; - /** @hide */ public static void getMovedToGlobalSettings(Set<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); @@ -2730,64 +2701,35 @@ public final class Settings { putIntForUser(cr, SHOW_GTALK_SERVICE_STATUS, flag ? 1 : 0, userHandle); } - private static final class DiscreteValueValidator implements Validator { - private final String[] mValues; - - public DiscreteValueValidator(String[] values) { - mValues = values; - } - - @Override - public boolean validate(String value) { - return ArrayUtils.contains(mValues, value); - } - } - - private static final class InclusiveIntegerRangeValidator implements Validator { - private final int mMin; - private final int mMax; - - public InclusiveIntegerRangeValidator(int min, int max) { - mMin = min; - mMax = max; - } - - @Override - public boolean validate(String value) { - try { - final int intValue = Integer.parseInt(value); - return intValue >= mMin && intValue <= mMax; - } catch (NumberFormatException e) { - return false; - } - } - } - - private static final class InclusiveFloatRangeValidator implements Validator { - private final float mMin; - private final float mMax; - - public InclusiveFloatRangeValidator(float min, float max) { - mMin = min; - mMax = max; - } + /** + * @deprecated Use {@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} instead + */ + @Deprecated + public static final String STAY_ON_WHILE_PLUGGED_IN = Global.STAY_ON_WHILE_PLUGGED_IN; + private static final Validator STAY_ON_WHILE_PLUGGED_IN_VALIDATOR = new Validator() { @Override public boolean validate(String value) { try { - final float floatValue = Float.parseFloat(value); - return floatValue >= mMin && floatValue <= mMax; + int val = Integer.parseInt(value); + return (val == 0) + || (val == BatteryManager.BATTERY_PLUGGED_AC) + || (val == BatteryManager.BATTERY_PLUGGED_USB) + || (val == BatteryManager.BATTERY_PLUGGED_WIRELESS) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_USB)) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_WIRELESS)) + || (val == (BatteryManager.BATTERY_PLUGGED_USB + | BatteryManager.BATTERY_PLUGGED_WIRELESS)) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_USB + | BatteryManager.BATTERY_PLUGGED_WIRELESS)); } catch (NumberFormatException e) { return false; } } - } - - /** - * @deprecated Use {@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} instead - */ - @Deprecated - public static final String STAY_ON_WHILE_PLUGGED_IN = Global.STAY_ON_WHILE_PLUGGED_IN; + }; /** * What happens when the user presses the end call button if they're not @@ -2802,7 +2744,7 @@ public final class Settings { public static final String END_BUTTON_BEHAVIOR = "end_button_behavior"; private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR = - new InclusiveIntegerRangeValidator(0, 3); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** * END_BUTTON_BEHAVIOR value for "go home". @@ -2828,7 +2770,7 @@ public final class Settings { */ public static final String ADVANCED_SETTINGS = "advanced_settings"; - private static final Validator ADVANCED_SETTINGS_VALIDATOR = sBooleanValidator; + private static final Validator ADVANCED_SETTINGS_VALIDATOR = BOOLEAN_VALIDATOR; /** * ADVANCED_SETTINGS default value. @@ -2929,7 +2871,7 @@ public final class Settings { @Deprecated public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip"; - private static final Validator WIFI_USE_STATIC_IP_VALIDATOR = sBooleanValidator; + private static final Validator WIFI_USE_STATIC_IP_VALIDATOR = BOOLEAN_VALIDATOR; /** * The static IP address. @@ -2941,7 +2883,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_IP = "wifi_static_ip"; - private static final Validator WIFI_STATIC_IP_VALIDATOR = sLenientIpAddressValidator; + private static final Validator WIFI_STATIC_IP_VALIDATOR = LENIENT_IP_ADDRESS_VALIDATOR; /** * If using static IP, the gateway's IP address. @@ -2953,7 +2895,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway"; - private static final Validator WIFI_STATIC_GATEWAY_VALIDATOR = sLenientIpAddressValidator; + private static final Validator WIFI_STATIC_GATEWAY_VALIDATOR = LENIENT_IP_ADDRESS_VALIDATOR; /** * If using static IP, the net mask. @@ -2965,7 +2907,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask"; - private static final Validator WIFI_STATIC_NETMASK_VALIDATOR = sLenientIpAddressValidator; + private static final Validator WIFI_STATIC_NETMASK_VALIDATOR = LENIENT_IP_ADDRESS_VALIDATOR; /** * If using static IP, the primary DNS's IP address. @@ -2977,7 +2919,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1"; - private static final Validator WIFI_STATIC_DNS1_VALIDATOR = sLenientIpAddressValidator; + private static final Validator WIFI_STATIC_DNS1_VALIDATOR = LENIENT_IP_ADDRESS_VALIDATOR; /** * If using static IP, the secondary DNS's IP address. @@ -2989,7 +2931,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2"; - private static final Validator WIFI_STATIC_DNS2_VALIDATOR = sLenientIpAddressValidator; + private static final Validator WIFI_STATIC_DNS2_VALIDATOR = LENIENT_IP_ADDRESS_VALIDATOR; /** * Determines whether remote devices may discover and/or connect to @@ -3003,7 +2945,7 @@ public final class Settings { "bluetooth_discoverability"; private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR = - new InclusiveIntegerRangeValidator(0, 2); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 2); /** * Bluetooth discoverability timeout. If this value is nonzero, then @@ -3014,7 +2956,7 @@ public final class Settings { "bluetooth_discoverability_timeout"; private static final Validator BLUETOOTH_DISCOVERABILITY_TIMEOUT_VALIDATOR = - sNonNegativeIntegerValidator; + NON_NEGATIVE_INTEGER_VALIDATOR; /** * @deprecated Use {@link android.provider.Settings.Secure#LOCK_PATTERN_ENABLED} @@ -3110,7 +3052,7 @@ public final class Settings { @Deprecated public static final String DIM_SCREEN = "dim_screen"; - private static final Validator DIM_SCREEN_VALIDATOR = sBooleanValidator; + private static final Validator DIM_SCREEN_VALIDATOR = BOOLEAN_VALIDATOR; /** * The display color mode. @@ -3130,7 +3072,8 @@ public final class Settings { */ public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; - private static final Validator SCREEN_OFF_TIMEOUT_VALIDATOR = sNonNegativeIntegerValidator; + private static final Validator SCREEN_OFF_TIMEOUT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; /** * The screen backlight brightness between 0 and 255. @@ -3138,7 +3081,7 @@ public final class Settings { public static final String SCREEN_BRIGHTNESS = "screen_brightness"; private static final Validator SCREEN_BRIGHTNESS_VALIDATOR = - new InclusiveIntegerRangeValidator(0, 255); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 255); /** * The screen backlight brightness between 0 and 255. @@ -3147,14 +3090,14 @@ public final class Settings { public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr"; private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR = - new InclusiveIntegerRangeValidator(0, 255); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 255); /** * Control whether to enable automatic brightness mode. */ public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; - private static final Validator SCREEN_BRIGHTNESS_MODE_VALIDATOR = sBooleanValidator; + private static final Validator SCREEN_BRIGHTNESS_MODE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Adjustment to auto-brightness to make it generally more (>0.0 <1.0) @@ -3164,7 +3107,7 @@ public final class Settings { public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj"; private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR = - new InclusiveFloatRangeValidator(-1, 1); + new SettingsValidators.InclusiveFloatRangeValidator(-1, 1); /** * SCREEN_BRIGHTNESS_MODE value for manual mode. @@ -3203,7 +3146,7 @@ public final class Settings { public static final String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected"; private static final Validator MODE_RINGER_STREAMS_AFFECTED_VALIDATOR = - sNonNegativeIntegerValidator; + NON_NEGATIVE_INTEGER_VALIDATOR; /** * Determines which streams are affected by mute. The @@ -3213,7 +3156,7 @@ public final class Settings { public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected"; private static final Validator MUTE_STREAMS_AFFECTED_VALIDATOR = - sNonNegativeIntegerValidator; + NON_NEGATIVE_INTEGER_VALIDATOR; /** * Whether vibrate is on for different events. This is used internally, @@ -3221,7 +3164,7 @@ public final class Settings { */ public static final String VIBRATE_ON = "vibrate_on"; - private static final Validator VIBRATE_ON_VALIDATOR = sBooleanValidator; + private static final Validator VIBRATE_ON_VALIDATOR = BOOLEAN_VALIDATOR; /** * If 1, redirects the system vibrator to all currently attached input devices @@ -3237,7 +3180,7 @@ public final class Settings { */ public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices"; - private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = sBooleanValidator; + private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR; /** * Ringer volume. This is used internally, changing this value will not @@ -3316,7 +3259,7 @@ public final class Settings { */ public static final String MASTER_MONO = "master_mono"; - private static final Validator MASTER_MONO_VALIDATOR = sBooleanValidator; + private static final Validator MASTER_MONO_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the notifications should use the ring volume (value of 1) or @@ -3336,7 +3279,7 @@ public final class Settings { public static final String NOTIFICATIONS_USE_RING_VOLUME = "notifications_use_ring_volume"; - private static final Validator NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR = sBooleanValidator; + private static final Validator NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether silent mode should allow vibration feedback. This is used @@ -3352,7 +3295,7 @@ public final class Settings { */ public static final String VIBRATE_IN_SILENT = "vibrate_in_silent"; - private static final Validator VIBRATE_IN_SILENT_VALIDATOR = sBooleanValidator; + private static final Validator VIBRATE_IN_SILENT_VALIDATOR = BOOLEAN_VALIDATOR; /** * The mapping of stream type (integer) to its setting. @@ -3400,7 +3343,7 @@ public final class Settings { */ public static final String RINGTONE = "ringtone"; - private static final Validator RINGTONE_VALIDATOR = sUriValidator; + private static final Validator RINGTONE_VALIDATOR = URI_VALIDATOR; /** * A {@link Uri} that will point to the current default ringtone at any @@ -3425,7 +3368,7 @@ public final class Settings { */ public static final String NOTIFICATION_SOUND = "notification_sound"; - private static final Validator NOTIFICATION_SOUND_VALIDATOR = sUriValidator; + private static final Validator NOTIFICATION_SOUND_VALIDATOR = URI_VALIDATOR; /** * A {@link Uri} that will point to the current default notification @@ -3448,7 +3391,7 @@ public final class Settings { */ public static final String ALARM_ALERT = "alarm_alert"; - private static final Validator ALARM_ALERT_VALIDATOR = sUriValidator; + private static final Validator ALARM_ALERT_VALIDATOR = URI_VALIDATOR; /** * A {@link Uri} that will point to the current default alarm alert at @@ -3470,31 +3413,21 @@ public final class Settings { */ public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver"; - private static final Validator MEDIA_BUTTON_RECEIVER_VALIDATOR = new Validator() { - @Override - public boolean validate(String value) { - try { - ComponentName.unflattenFromString(value); - return true; - } catch (NullPointerException e) { - return false; - } - } - }; + private static final Validator MEDIA_BUTTON_RECEIVER_VALIDATOR = COMPONENT_NAME_VALIDATOR; /** * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off */ public static final String TEXT_AUTO_REPLACE = "auto_replace"; - private static final Validator TEXT_AUTO_REPLACE_VALIDATOR = sBooleanValidator; + private static final Validator TEXT_AUTO_REPLACE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Setting to enable Auto Caps in text editors. 1 = On, 0 = Off */ public static final String TEXT_AUTO_CAPS = "auto_caps"; - private static final Validator TEXT_AUTO_CAPS_VALIDATOR = sBooleanValidator; + private static final Validator TEXT_AUTO_CAPS_VALIDATOR = BOOLEAN_VALIDATOR; /** * Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This @@ -3502,19 +3435,19 @@ public final class Settings { */ public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate"; - private static final Validator TEXT_AUTO_PUNCTUATE_VALIDATOR = sBooleanValidator; + private static final Validator TEXT_AUTO_PUNCTUATE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Setting to showing password characters in text editors. 1 = On, 0 = Off */ public static final String TEXT_SHOW_PASSWORD = "show_password"; - private static final Validator TEXT_SHOW_PASSWORD_VALIDATOR = sBooleanValidator; + private static final Validator TEXT_SHOW_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR; public static final String SHOW_GTALK_SERVICE_STATUS = "SHOW_GTALK_SERVICE_STATUS"; - private static final Validator SHOW_GTALK_SERVICE_STATUS_VALIDATOR = sBooleanValidator; + private static final Validator SHOW_GTALK_SERVICE_STATUS_VALIDATOR = BOOLEAN_VALIDATOR; /** * Name of activity to use for wallpaper on the home screen. @@ -3543,6 +3476,8 @@ public final class Settings { @Deprecated public static final String AUTO_TIME = Global.AUTO_TIME; + private static final Validator AUTO_TIME_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#AUTO_TIME_ZONE} * instead @@ -3550,6 +3485,8 @@ public final class Settings { @Deprecated public static final String AUTO_TIME_ZONE = Global.AUTO_TIME_ZONE; + private static final Validator AUTO_TIME_ZONE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Display times as 12 or 24 hours * 12 @@ -3559,7 +3496,7 @@ public final class Settings { /** @hide */ public static final Validator TIME_12_24_VALIDATOR = - new DiscreteValueValidator(new String[] {"12", "24", null}); + new SettingsValidators.DiscreteValueValidator(new String[] {"12", "24", null}); /** * Date format string @@ -3592,7 +3529,7 @@ public final class Settings { public static final String SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run"; /** @hide */ - public static final Validator SETUP_WIZARD_HAS_RUN_VALIDATOR = sBooleanValidator; + public static final Validator SETUP_WIZARD_HAS_RUN_VALIDATOR = BOOLEAN_VALIDATOR; /** * Scaling factor for normal window animations. Setting to 0 will disable window @@ -3631,7 +3568,7 @@ public final class Settings { public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; /** @hide */ - public static final Validator ACCELEROMETER_ROTATION_VALIDATOR = sBooleanValidator; + public static final Validator ACCELEROMETER_ROTATION_VALIDATOR = BOOLEAN_VALIDATOR; /** * Default screen rotation when no other policy applies. @@ -3645,7 +3582,7 @@ public final class Settings { /** @hide */ public static final Validator USER_ROTATION_VALIDATOR = - new InclusiveIntegerRangeValidator(0, 3); + new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** * Control whether the rotation lock toggle in the System UI should be hidden. @@ -3663,7 +3600,7 @@ public final class Settings { /** @hide */ public static final Validator HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY_VALIDATOR = - sBooleanValidator; + BOOLEAN_VALIDATOR; /** * Whether the phone vibrates when it is ringing due to an incoming call. This will @@ -3678,7 +3615,7 @@ public final class Settings { public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; /** @hide */ - public static final Validator VIBRATE_WHEN_RINGING_VALIDATOR = sBooleanValidator; + public static final Validator VIBRATE_WHEN_RINGING_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the audible DTMF tones are played by the dialer when dialing. The value is @@ -3687,7 +3624,7 @@ public final class Settings { public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; /** @hide */ - public static final Validator DTMF_TONE_WHEN_DIALING_VALIDATOR = sBooleanValidator; + public static final Validator DTMF_TONE_WHEN_DIALING_VALIDATOR = BOOLEAN_VALIDATOR; /** * CDMA only settings @@ -3698,7 +3635,7 @@ public final class Settings { public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; /** @hide */ - public static final Validator DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR = sBooleanValidator; + public static final Validator DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the hearing aid is enabled. The value is @@ -3708,7 +3645,7 @@ public final class Settings { public static final String HEARING_AID = "hearing_aid"; /** @hide */ - public static final Validator HEARING_AID_VALIDATOR = sBooleanValidator; + public static final Validator HEARING_AID_VALIDATOR = BOOLEAN_VALIDATOR; /** * CDMA only settings @@ -3722,7 +3659,8 @@ public final class Settings { public static final String TTY_MODE = "tty_mode"; /** @hide */ - public static final Validator TTY_MODE_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3); + public static final Validator TTY_MODE_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is @@ -3731,7 +3669,7 @@ public final class Settings { public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; /** @hide */ - public static final Validator SOUND_EFFECTS_ENABLED_VALIDATOR = sBooleanValidator; + public static final Validator SOUND_EFFECTS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the haptic feedback (long presses, ...) are enabled. The value is @@ -3740,7 +3678,7 @@ public final class Settings { public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; /** @hide */ - public static final Validator HAPTIC_FEEDBACK_ENABLED_VALIDATOR = sBooleanValidator; + public static final Validator HAPTIC_FEEDBACK_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** * @deprecated Each application that shows web suggestions should have its own @@ -3750,7 +3688,7 @@ public final class Settings { public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; /** @hide */ - public static final Validator SHOW_WEB_SUGGESTIONS_VALIDATOR = sBooleanValidator; + public static final Validator SHOW_WEB_SUGGESTIONS_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the notification LED should repeatedly flash when a notification is @@ -3760,7 +3698,7 @@ public final class Settings { public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; /** @hide */ - public static final Validator NOTIFICATION_LIGHT_PULSE_VALIDATOR = sBooleanValidator; + public static final Validator NOTIFICATION_LIGHT_PULSE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Show pointer location on screen? @@ -3771,7 +3709,7 @@ public final class Settings { public static final String POINTER_LOCATION = "pointer_location"; /** @hide */ - public static final Validator POINTER_LOCATION_VALIDATOR = sBooleanValidator; + public static final Validator POINTER_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR; /** * Show touch positions on screen? @@ -3782,7 +3720,7 @@ public final class Settings { public static final String SHOW_TOUCHES = "show_touches"; /** @hide */ - public static final Validator SHOW_TOUCHES_VALIDATOR = sBooleanValidator; + public static final Validator SHOW_TOUCHES_VALIDATOR = BOOLEAN_VALIDATOR; /** * Log raw orientation data from @@ -3796,7 +3734,7 @@ public final class Settings { "window_orientation_listener_log"; /** @hide */ - public static final Validator WINDOW_ORIENTATION_LISTENER_LOG_VALIDATOR = sBooleanValidator; + public static final Validator WINDOW_ORIENTATION_LISTENER_LOG_VALIDATOR = BOOLEAN_VALIDATOR; /** * @deprecated Use {@link android.provider.Settings.Global#POWER_SOUNDS_ENABLED} @@ -3806,6 +3744,8 @@ public final class Settings { @Deprecated public static final String POWER_SOUNDS_ENABLED = Global.POWER_SOUNDS_ENABLED; + private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#DOCK_SOUNDS_ENABLED} * instead @@ -3814,6 +3754,8 @@ public final class Settings { @Deprecated public static final String DOCK_SOUNDS_ENABLED = Global.DOCK_SOUNDS_ENABLED; + private static final Validator DOCK_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether to play sounds when the keyguard is shown and dismissed. * @hide @@ -3821,7 +3763,7 @@ public final class Settings { public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled"; /** @hide */ - public static final Validator LOCKSCREEN_SOUNDS_ENABLED_VALIDATOR = sBooleanValidator; + public static final Validator LOCKSCREEN_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the lockscreen should be completely disabled. @@ -3830,7 +3772,7 @@ public final class Settings { public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled"; /** @hide */ - public static final Validator LOCKSCREEN_DISABLED_VALIDATOR = sBooleanValidator; + public static final Validator LOCKSCREEN_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** * @deprecated Use {@link android.provider.Settings.Global#LOW_BATTERY_SOUND} @@ -3897,7 +3839,7 @@ public final class Settings { public static final String SIP_RECEIVE_CALLS = "sip_receive_calls"; /** @hide */ - public static final Validator SIP_RECEIVE_CALLS_VALIDATOR = sBooleanValidator; + public static final Validator SIP_RECEIVE_CALLS_VALIDATOR = BOOLEAN_VALIDATOR; /** * Call Preference String. @@ -3908,8 +3850,9 @@ public final class Settings { public static final String SIP_CALL_OPTIONS = "sip_call_options"; /** @hide */ - public static final Validator SIP_CALL_OPTIONS_VALIDATOR = new DiscreteValueValidator( - new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"}); + public static final Validator SIP_CALL_OPTIONS_VALIDATOR = + new SettingsValidators.DiscreteValueValidator( + new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"}); /** * One of the sip call options: Always use SIP with network access. @@ -3918,7 +3861,7 @@ public final class Settings { public static final String SIP_ALWAYS = "SIP_ALWAYS"; /** @hide */ - public static final Validator SIP_ALWAYS_VALIDATOR = sBooleanValidator; + public static final Validator SIP_ALWAYS_VALIDATOR = BOOLEAN_VALIDATOR; /** * One of the sip call options: Only if destination is a SIP address. @@ -3927,7 +3870,7 @@ public final class Settings { public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY"; /** @hide */ - public static final Validator SIP_ADDRESS_ONLY_VALIDATOR = sBooleanValidator; + public static final Validator SIP_ADDRESS_ONLY_VALIDATOR = BOOLEAN_VALIDATOR; /** * @deprecated Use SIP_ALWAYS or SIP_ADDRESS_ONLY instead. Formerly used to indicate that @@ -3940,7 +3883,7 @@ public final class Settings { public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME"; /** @hide */ - public static final Validator SIP_ASK_ME_EACH_TIME_VALIDATOR = sBooleanValidator; + public static final Validator SIP_ASK_ME_EACH_TIME_VALIDATOR = BOOLEAN_VALIDATOR; /** * Pointer speed setting. @@ -3954,7 +3897,7 @@ public final class Settings { /** @hide */ public static final Validator POINTER_SPEED_VALIDATOR = - new InclusiveFloatRangeValidator(-7, 7); + new SettingsValidators.InclusiveFloatRangeValidator(-7, 7); /** * Whether lock-to-app will be triggered by long-press on recents. @@ -3963,7 +3906,7 @@ public final class Settings { public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled"; /** @hide */ - public static final Validator LOCK_TO_APP_ENABLED_VALIDATOR = sBooleanValidator; + public static final Validator LOCK_TO_APP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** * I am the lolrus. @@ -3995,7 +3938,7 @@ public final class Settings { public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent"; /** @hide */ - private static final Validator SHOW_BATTERY_PERCENT_VALIDATOR = sBooleanValidator; + private static final Validator SHOW_BATTERY_PERCENT_VALIDATOR = BOOLEAN_VALIDATOR; /** * IMPORTANT: If you add a new public settings you also have to add it to @@ -4067,6 +4010,9 @@ public final class Settings { * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * * @hide */ public static final String[] LEGACY_RESTORE_SETTINGS = { @@ -4175,11 +4121,15 @@ public final class Settings { /** * These are all public system settings * + * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator, + * otherwise they won't be restored. + * * @hide */ public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); static { - VALIDATORS.put(END_BUTTON_BEHAVIOR,END_BUTTON_BEHAVIOR_VALIDATOR); + VALIDATORS.put(STAY_ON_WHILE_PLUGGED_IN, STAY_ON_WHILE_PLUGGED_IN_VALIDATOR); + VALIDATORS.put(END_BUTTON_BEHAVIOR, END_BUTTON_BEHAVIOR_VALIDATOR); VALIDATORS.put(WIFI_USE_STATIC_IP, WIFI_USE_STATIC_IP_VALIDATOR); VALIDATORS.put(BLUETOOTH_DISCOVERABILITY, BLUETOOTH_DISCOVERABILITY_VALIDATOR); VALIDATORS.put(BLUETOOTH_DISCOVERABILITY_TIMEOUT, @@ -4201,6 +4151,8 @@ public final class Settings { VALIDATORS.put(TEXT_AUTO_CAPS, TEXT_AUTO_CAPS_VALIDATOR); VALIDATORS.put(TEXT_AUTO_PUNCTUATE, TEXT_AUTO_PUNCTUATE_VALIDATOR); VALIDATORS.put(TEXT_SHOW_PASSWORD, TEXT_SHOW_PASSWORD_VALIDATOR); + VALIDATORS.put(AUTO_TIME, AUTO_TIME_VALIDATOR); + VALIDATORS.put(AUTO_TIME_ZONE, AUTO_TIME_ZONE_VALIDATOR); VALIDATORS.put(SHOW_GTALK_SERVICE_STATUS, SHOW_GTALK_SERVICE_STATUS_VALIDATOR); VALIDATORS.put(WALLPAPER_ACTIVITY, WALLPAPER_ACTIVITY_VALIDATOR); VALIDATORS.put(TIME_12_24, TIME_12_24_VALIDATOR); @@ -4211,6 +4163,8 @@ public final class Settings { VALIDATORS.put(DTMF_TONE_WHEN_DIALING, DTMF_TONE_WHEN_DIALING_VALIDATOR); VALIDATORS.put(SOUND_EFFECTS_ENABLED, SOUND_EFFECTS_ENABLED_VALIDATOR); VALIDATORS.put(HAPTIC_FEEDBACK_ENABLED, HAPTIC_FEEDBACK_ENABLED_VALIDATOR); + VALIDATORS.put(POWER_SOUNDS_ENABLED, POWER_SOUNDS_ENABLED_VALIDATOR); + VALIDATORS.put(DOCK_SOUNDS_ENABLED, DOCK_SOUNDS_ENABLED_VALIDATOR); VALIDATORS.put(SHOW_WEB_SUGGESTIONS, SHOW_WEB_SUGGESTIONS_VALIDATOR); VALIDATORS.put(WIFI_USE_STATIC_IP, WIFI_USE_STATIC_IP_VALIDATOR); VALIDATORS.put(END_BUTTON_BEHAVIOR, END_BUTTON_BEHAVIOR_VALIDATOR); @@ -4335,6 +4289,8 @@ public final class Settings { @Deprecated public static final String BLUETOOTH_ON = Global.BLUETOOTH_ON; + private static final Validator BLUETOOTH_ON_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#DATA_ROAMING} instead */ @@ -4412,6 +4368,8 @@ public final class Settings { @Deprecated public static final String USB_MASS_STORAGE_ENABLED = Global.USB_MASS_STORAGE_ENABLED; + private static final Validator USB_MASS_STORAGE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#USE_GOOGLE_MAIL} instead */ @@ -4441,6 +4399,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON; + private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * @deprecated Use * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} instead @@ -4449,6 +4410,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead @@ -4456,6 +4420,9 @@ public final class Settings { @Deprecated public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead */ @@ -5218,6 +5185,8 @@ public final class Settings { @Deprecated public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; + private static final Validator BUGREPORT_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#ADB_ENABLED} instead */ @@ -5235,6 +5204,8 @@ public final class Settings { @Deprecated public static final String ALLOW_MOCK_LOCATION = "mock_location"; + private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR; + /** * On Android 8.0 (API level 26) and higher versions of the platform, * a 64-bit number (expressed as a hexadecimal string), unique to @@ -5280,6 +5251,8 @@ public final class Settings { @Deprecated public static final String BLUETOOTH_ON = Global.BLUETOOTH_ON; + private static final Validator BLUETOOTH_ON_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#DATA_ROAMING} instead */ @@ -5327,6 +5300,8 @@ public final class Settings { @TestApi public static final String AUTOFILL_SERVICE = "autofill_service"; + private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR; + /** * Boolean indicating if Autofill supports field classification. * @@ -5413,9 +5388,38 @@ public final class Settings { * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. + * + * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0" + * where imeId is ComponentName and subtype is int32. */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; + private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] inputMethods = value.split(":"); + boolean valid = true; + for (String inputMethod : inputMethods) { + if (inputMethod.length() == 0) { + return false; + } + String[] subparts = inputMethod.split(";"); + for (String subpart : subparts) { + // allow either a non negative integer or a ComponentName + valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart) + || COMPONENT_NAME_VALIDATOR.validate(subpart)); + } + if (!valid) { + return false; + } + } + return valid; + } + }; + /** * List of system input methods that are currently disabled. This is a string * containing the IDs of all disabled input methods, each ID separated @@ -5431,6 +5435,8 @@ public final class Settings { */ public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; + private static final Validator SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Host name and port for global http proxy. Uses ':' seperator for * between host and port. @@ -5707,6 +5713,8 @@ public final class Settings { @Deprecated public static final String USB_MASS_STORAGE_ENABLED = Global.USB_MASS_STORAGE_ENABLED; + private static final Validator USB_MASS_STORAGE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#USE_GOOGLE_MAIL} instead */ @@ -5718,6 +5726,8 @@ public final class Settings { */ public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled"; + private static final Validator ACCESSIBILITY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut is enabled. * @hide @@ -5725,6 +5735,8 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_ENABLED = "accessibility_shortcut_enabled"; + private static final Validator ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut is enabled. * @hide @@ -5732,6 +5744,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN = "accessibility_shortcut_on_lock_screen"; + private static final Validator ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting specifying if the accessibility shortcut dialog has been shown to this user. * @hide @@ -5739,6 +5754,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN = "accessibility_shortcut_dialog_shown"; + private static final Validator ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting specifying the accessibility service to be toggled via the accessibility * shortcut. Must be its flattened {@link ComponentName}. @@ -5747,6 +5765,9 @@ public final class Settings { public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; + private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** * Setting specifying the accessibility service or feature to be toggled via the * accessibility button in the navigation bar. This is either a flattened @@ -5757,17 +5778,32 @@ public final class Settings { public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT = "accessibility_button_target_component"; + private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR = + new Validator() { + @Override + public boolean validate(String value) { + // technically either ComponentName or class name, but there's proper value + // validation at callsites, so allow any non-null string + return value != null; + } + }; + /** * If touch exploration is enabled. */ public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled"; + private static final Validator TOUCH_EXPLORATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * List of the enabled accessibility providers. */ public static final String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services"; + private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * List of the accessibility services to which the user has granted * permission to put the device into touch exploration mode. @@ -5777,6 +5813,9 @@ public final class Settings { public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES = "touch_exploration_granted_accessibility_services"; + private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Uri of the slice that's presented on the keyguard. * Defaults to a slice with the date and next alarm. @@ -5795,6 +5834,8 @@ public final class Settings { @Deprecated public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; + private static final Validator ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether to draw text with high contrast while in accessibility mode. * @@ -5803,6 +5844,9 @@ public final class Settings { public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED = "high_text_contrast_enabled"; + private static final Validator ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether the display magnification is enabled via a system-wide * triple tap gesture. Display magnifications allows the user to zoom in the display content @@ -5815,6 +5859,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether the display magnification is enabled via a shortcut * affordance within the system's navigation area. Display magnifications allows the user to @@ -5826,6 +5873,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR + = BOOLEAN_VALIDATOR; + /** * Setting that specifies what the display magnification scale is. * Display magnifications allows the user to zoom in the display @@ -5839,6 +5889,9 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE = "accessibility_display_magnification_scale"; + private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE); + /** * Unused mangnification setting * @@ -5891,6 +5944,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_ENABLED = "accessibility_captioning_enabled"; + private static final Validator ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies the language for captions as a locale string, * e.g. en_US. @@ -5901,6 +5957,8 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_LOCALE = "accessibility_captioning_locale"; + private static final Validator ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR = LOCALE_VALIDATOR; + /** * Integer property that specifies the preset style for captions, one * of: @@ -5915,6 +5973,10 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_PRESET = "accessibility_captioning_preset"; + private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2", + "3", "4"}); + /** * Integer property that specifes the background color for captions as a * packed 32-bit color. @@ -5925,6 +5987,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR = "accessibility_captioning_background_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the foreground color for captions as a * packed 32-bit color. @@ -5935,6 +6000,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR = "accessibility_captioning_foreground_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the edge type for captions, one of: * <ul> @@ -5949,6 +6017,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE = "accessibility_captioning_edge_type"; + private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"}); + /** * Integer property that specifes the edge color for captions as a * packed 32-bit color. @@ -5960,6 +6031,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR = "accessibility_captioning_edge_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * Integer property that specifes the window color for captions as a * packed 32-bit color. @@ -5970,6 +6044,9 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR = "accessibility_captioning_window_color"; + private static final Validator ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR = + ANY_INTEGER_VALIDATOR; + /** * String property that specifies the typeface for captions, one of: * <ul> @@ -5985,6 +6062,10 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE = "accessibility_captioning_typeface"; + private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT", + "MONOSPACE", "SANS_SERIF", "SERIF"}); + /** * Floating point property that specifies font scaling for captions. * @@ -5993,12 +6074,18 @@ public final class Settings { public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE = "accessibility_captioning_font_scale"; + private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f); + /** * Setting that specifies whether display color inversion is enabled. */ public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Setting that specifies whether display color space adjustment is * enabled. @@ -6008,15 +6095,24 @@ public final class Settings { public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED = "accessibility_display_daltonizer_enabled"; + private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Integer property that specifies the type of color space adjustment to - * perform. Valid values are defined in AccessibilityManager. + * perform. Valid values are defined in AccessibilityManager: + * - AccessibilityManager.DALTONIZER_DISABLED = -1 + * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0 + * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12 * * @hide */ public static final String ACCESSIBILITY_DISPLAY_DALTONIZER = "accessibility_display_daltonizer"; + private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"}); + /** * Setting that specifies whether automatic click when the mouse pointer stops moving is * enabled. @@ -6026,6 +6122,9 @@ public final class Settings { public static final String ACCESSIBILITY_AUTOCLICK_ENABLED = "accessibility_autoclick_enabled"; + private static final Validator ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Integer setting specifying amount of time in ms the mouse pointer has to stay still * before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set. @@ -6036,6 +6135,9 @@ public final class Settings { public static final String ACCESSIBILITY_AUTOCLICK_DELAY = "accessibility_autoclick_delay"; + private static final Validator ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Whether or not larger size icons are used for the pointer of mouse/trackpad for * accessibility. @@ -6045,12 +6147,18 @@ public final class Settings { public static final String ACCESSIBILITY_LARGE_POINTER_ICON = "accessibility_large_pointer_icon"; + private static final Validator ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * The timeout for considering a press to be a long press in milliseconds. * @hide */ public static final String LONG_PRESS_TIMEOUT = "long_press_timeout"; + private static final Validator LONG_PRESS_TIMEOUT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * The duration in milliseconds between the first tap's up event and the second tap's * down event for an interaction to be considered part of the same multi-press. @@ -6104,16 +6212,22 @@ public final class Settings { */ public static final String TTS_DEFAULT_RATE = "tts_default_rate"; + private static final Validator TTS_DEFAULT_RATE_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default text-to-speech engine pitch. 100 = 1x */ public static final String TTS_DEFAULT_PITCH = "tts_default_pitch"; + private static final Validator TTS_DEFAULT_PITCH_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default text-to-speech engine. */ public static final String TTS_DEFAULT_SYNTH = "tts_default_synth"; + private static final Validator TTS_DEFAULT_SYNTH_VALIDATOR = PACKAGE_NAME_VALIDATOR; + /** * Default text-to-speech language. * @@ -6161,11 +6275,33 @@ public final class Settings { */ public static final String TTS_DEFAULT_LOCALE = "tts_default_locale"; + private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.length() == 0) { + return false; + } + String[] ttsLocales = value.split(","); + boolean valid = true; + for (String ttsLocale : ttsLocales) { + String[] parts = ttsLocale.split(":"); + valid |= ((parts.length == 2) + && (parts[0].length() > 0) + && ANY_STRING_VALIDATOR.validate(parts[0]) + && LOCALE_VALIDATOR.validate(parts[1])); + } + return valid; + } + }; + /** * Space delimited list of plugin packages that are enabled. */ public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins"; + private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR = + new SettingsValidators.PackageNameListValidator(" "); + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON} * instead. @@ -6174,6 +6310,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON; + private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} * instead. @@ -6182,6 +6321,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT} * instead. @@ -6190,6 +6332,9 @@ public final class Settings { public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} * instead. @@ -6348,6 +6493,9 @@ public final class Settings { public static final String PREFERRED_TTY_MODE = "preferred_tty_mode"; + private static final Validator PREFERRED_TTY_MODE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"}); + /** * Whether the enhanced voice privacy mode is enabled. * 0 = normal voice privacy @@ -6356,6 +6504,8 @@ public final class Settings { */ public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled"; + private static final Validator ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the TTY mode mode is enabled. * 0 = disabled @@ -6364,6 +6514,8 @@ public final class Settings { */ public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; + private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Controls whether settings backup is enabled. * Type: int ( 0 = disabled, 1 = enabled ) @@ -6534,24 +6686,32 @@ public final class Settings { */ public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd"; + private static final Validator MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart"; + private static final Validator MOUNT_UMS_AUTOSTART_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt"; + private static final Validator MOUNT_UMS_PROMPT_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true) * @hide */ public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled"; + private static final Validator MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If nonzero, ANRs in invisible background processes bring up a dialog. * Otherwise, the process will be silently killed. @@ -6562,6 +6722,17 @@ public final class Settings { public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; /** + * If nonzero, crashes in foreground processes will bring up a dialog. + * Otherwise, the process will be silently killed. + * @hide + */ + public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = + "show_first_crash_dialog_dev_option"; + + private static final Validator SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR = + BOOLEAN_VALIDATOR; + + /** * The {@link ComponentName} string of the service to be used as the voice recognition * service. * @@ -6586,6 +6757,8 @@ public final class Settings { */ public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker"; + private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR; + /** * The {@link ComponentName} string of the selected subtype of the selected spell checker * service which is one of the services managed by the text service manager. @@ -6595,13 +6768,18 @@ public final class Settings { public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype"; + private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** - * The {@link ComponentName} string whether spell checker is enabled or not. + * Whether spell checker is enabled or not. * * @hide */ public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled"; + private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * What happens when the user presses the Power button while in-call * and the screen is on.<br/> @@ -6613,6 +6791,9 @@ public final class Settings { */ public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior"; + private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"}); + /** * INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen". * @hide @@ -6668,12 +6849,16 @@ public final class Settings { */ public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled"; + private static final Validator WAKE_GESTURE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the device should doze if configured. * @hide */ public static final String DOZE_ENABLED = "doze_enabled"; + private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether doze should be always on. * @hide @@ -6686,6 +6871,8 @@ public final class Settings { */ public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up"; + private static final Validator DOZE_PULSE_ON_PICK_UP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the device should pulse on long press gesture. * @hide @@ -6698,6 +6885,8 @@ public final class Settings { */ public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap"; + private static final Validator DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per @@ -6712,6 +6901,8 @@ public final class Settings { */ public static final String SCREENSAVER_ENABLED = "screensaver_enabled"; + private static final Validator SCREENSAVER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The user's chosen screensaver components. * @@ -6721,6 +6912,9 @@ public final class Settings { */ public static final String SCREENSAVER_COMPONENTS = "screensaver_components"; + private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(","); + /** * If screensavers are enabled, whether the screensaver should be automatically launched * when the device is inserted into a (desk) dock. @@ -6728,6 +6922,8 @@ public final class Settings { */ public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock"; + private static final Validator SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If screensavers are enabled, whether the screensaver should be automatically launched * when the screen times out when not on battery. @@ -6735,6 +6931,8 @@ public final class Settings { */ public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep"; + private static final Validator SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If screensavers are enabled, the default screensaver component. * @hide @@ -6747,6 +6945,9 @@ public final class Settings { */ public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; + private static final Validator NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR = + COMPONENT_NAME_VALIDATOR; + /** * Whether NFC payment is handled by the foreground application or a default. * @hide @@ -6844,6 +7045,9 @@ public final class Settings { public static final String ENABLED_NOTIFICATION_ASSISTANT = "enabled_notification_assistant"; + private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Read only list of the service components that the current user has explicitly allowed to * see all of the user's notifications, separated by ':'. @@ -6855,6 +7059,9 @@ public final class Settings { @Deprecated public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners"; + private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Read only list of the packages that the current user has explicitly allowed to * manage do not disturb, separated by ':'. @@ -6867,6 +7074,9 @@ public final class Settings { public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; + private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR = + new SettingsValidators.PackageNameListValidator(":"); + /** * Defines whether managed profile ringtones should be synced from it's parent profile * <p> @@ -6880,6 +7090,8 @@ public final class Settings { @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; + private static final Validator SYNC_PARENT_SOUNDS_VALIDATOR = BOOLEAN_VALIDATOR; + /** @hide */ public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; @@ -6966,12 +7178,17 @@ public final class Settings { */ public static final String SLEEP_TIMEOUT = "sleep_timeout"; + private static final Validator SLEEP_TIMEOUT_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE); + /** * Controls whether double tap to wake is enabled. * @hide */ public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; + private static final Validator DOUBLE_TAP_TO_WAKE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The current assistant component. It could be a voice interaction service, * or an activity that handles ACTION_ASSIST, or empty which means using the default @@ -6988,6 +7205,8 @@ public final class Settings { */ public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled"; + private static final Validator CAMERA_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether the camera launch gesture to double tap the power button when the screen is off * should be disabled. @@ -6997,6 +7216,9 @@ public final class Settings { public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED = "camera_double_tap_power_gesture_disabled"; + private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether the camera double twist gesture to flip between front and back mode should be * enabled. @@ -7006,6 +7228,9 @@ public final class Settings { public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED = "camera_double_twist_to_flip_enabled"; + private static final Validator CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether or not the smart camera lift trigger that launches the camera when the user moves * the phone into a position for taking photos should be enabled. @@ -7028,6 +7253,9 @@ public final class Settings { */ public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled"; + private static final Validator ASSIST_GESTURE_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Sensitivity control for the assist gesture. * @@ -7035,6 +7263,9 @@ public final class Settings { */ public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity"; + private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR = + new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f); + /** * Whether the assist gesture should silence alerts. * @@ -7043,6 +7274,9 @@ public final class Settings { public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED = "assist_gesture_silence_alerts_enabled"; + private static final Validator ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether the assist gesture should wake the phone. * @@ -7051,6 +7285,9 @@ public final class Settings { public static final String ASSIST_GESTURE_WAKE_ENABLED = "assist_gesture_wake_enabled"; + private static final Validator ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * Whether Assist Gesture Deferred Setup has been completed * @@ -7058,6 +7295,8 @@ public final class Settings { */ public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete"; + private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Control whether Night display is currently activated. * @hide @@ -7070,6 +7309,8 @@ public final class Settings { */ public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode"; + private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Control the color temperature of Night Display, represented in Kelvin. * @hide @@ -7077,6 +7318,9 @@ public final class Settings { public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE = "night_display_color_temperature"; + private static final Validator NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Custom time when Night display is scheduled to activate. * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). @@ -7085,6 +7329,9 @@ public final class Settings { public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; + private static final Validator NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Custom time when Night display is scheduled to deactivate. * Represented as milliseconds from midnight (e.g. 21600000 == 6am). @@ -7092,6 +7339,9 @@ public final class Settings { */ public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; + private static final Validator NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * A String representing the LocalDateTime when Night display was last activated. Use to * decide whether to apply the current activated state after a reboot or user change. In @@ -7109,6 +7359,9 @@ public final class Settings { */ public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; + private static final Validator ENABLED_VR_LISTENERS_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(":"); + /** * Behavior of the display while in VR mode. * @@ -7118,6 +7371,9 @@ public final class Settings { */ public static final String VR_DISPLAY_MODE = "vr_display_mode"; + private static final Validator VR_DISPLAY_MODE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"}); + /** * Lower the display persistence while the system is in VR mode. * @@ -7174,6 +7430,9 @@ public final class Settings { public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN = "automatic_storage_manager_days_to_retain"; + private static final Validator AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Default number of days of information for the automatic storage manager to retain. * @@ -7215,12 +7474,30 @@ public final class Settings { public static final String SYSTEM_NAVIGATION_KEYS_ENABLED = "system_navigation_keys_enabled"; + private static final Validator SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Holds comma separated list of ordering of QS tiles. * @hide */ public static final String QS_TILES = "sysui_qs_tiles"; + private static final Validator QS_TILES_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] tiles = value.split(","); + boolean valid = true; + for (String tile : tiles) { + // tile can be any non-empty string as specified by OEM + valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile)); + } + return valid; + } + }; + /** * Specifies whether the web action API is enabled. * @@ -7256,18 +7533,38 @@ public final class Settings { */ public static final String NOTIFICATION_BADGING = "notification_badging"; + private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Comma separated list of QS tiles that have been auto-added already. * @hide */ public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles"; + private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] tiles = value.split(","); + boolean valid = true; + for (String tile : tiles) { + // tile can be any non-empty string as specified by OEM + valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile)); + } + return valid; + } + }; + /** * Whether the Lockdown button should be shown in the power menu. * @hide */ public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu"; + private static final Validator LOCKDOWN_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Backup manager behavioral parameters. * This is encoded as a key=value list, separated by commas. Ex: @@ -7307,8 +7604,6 @@ public final class Settings { public static final String[] SETTINGS_TO_BACKUP = { BUGREPORT_IN_POWER_MENU, // moved to global ALLOW_MOCK_LOCATION, - PARENTAL_CONTROL_ENABLED, - PARENTAL_CONTROL_REDIRECT_URL, USB_MASS_STORAGE_ENABLED, // moved to global ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, ACCESSIBILITY_DISPLAY_DALTONIZER, @@ -7340,12 +7635,9 @@ public final class Settings { ACCESSIBILITY_CAPTIONING_TYPEFACE, ACCESSIBILITY_CAPTIONING_FONT_SCALE, ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, - TTS_USE_DEFAULTS, TTS_DEFAULT_RATE, TTS_DEFAULT_PITCH, TTS_DEFAULT_SYNTH, - TTS_DEFAULT_LANG, - TTS_DEFAULT_COUNTRY, TTS_ENABLED_PLUGINS, TTS_DEFAULT_LOCALE, SHOW_IME_WITH_HARD_KEYBOARD, @@ -7398,9 +7690,161 @@ public final class Settings { SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_SLEEP, LOCKDOWN_IN_POWER_MENU, + SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, }; - /** @hide */ + /** + * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ + public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); + static { + VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR); + VALIDATORS.put(ALLOW_MOCK_LOCATION, ALLOW_MOCK_LOCATION_VALIDATOR); + VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, + ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER, + ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, + ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR); + VALIDATORS.put(AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR); + VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES, + ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR); + VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR); + VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR); + VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR); + VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_ENABLED, ACCESSIBILITY_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_BUTTON_TARGET_COMPONENT, + ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, + ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ENABLED, + ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, + ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_SPEAK_PASSWORD, ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, + ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_PRESET, + ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_ENABLED, + ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_LOCALE, + ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR, + ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR, + ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_TYPE, + ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_COLOR, + ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_TYPEFACE, + ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FONT_SCALE, + ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_CAPTIONING_WINDOW_COLOR, + ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_RATE, TTS_DEFAULT_RATE_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_PITCH, TTS_DEFAULT_PITCH_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_SYNTH, TTS_DEFAULT_SYNTH_VALIDATOR); + VALIDATORS.put(TTS_ENABLED_PLUGINS, TTS_ENABLED_PLUGINS_VALIDATOR); + VALIDATORS.put(TTS_DEFAULT_LOCALE, TTS_DEFAULT_LOCALE_VALIDATOR); + VALIDATORS.put(SHOW_IME_WITH_HARD_KEYBOARD, SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR); + VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR); + VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR); + VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR); + VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR); + VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE, + SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR); + VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR); + VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR); + VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR); + VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR); + VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR); + VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR); + VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR); + VALIDATORS.put(CAMERA_GESTURE_DISABLED, CAMERA_GESTURE_DISABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_ENABLED, + ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_DELAY, ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR); + VALIDATORS.put(ACCESSIBILITY_LARGE_POINTER_ICON, + ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR); + VALIDATORS.put(PREFERRED_TTY_MODE, PREFERRED_TTY_MODE_VALIDATOR); + VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED, + ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR); + VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR); + VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME, + NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE, + NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR); + VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR); + VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR); + VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, + CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR); + VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, + CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR); + VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED, + SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR); + VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR); + VALIDATORS.put(DOZE_ENABLED, DOZE_ENABLED_VALIDATOR); + VALIDATORS.put(DOZE_PULSE_ON_PICK_UP, DOZE_PULSE_ON_PICK_UP_VALIDATOR); + VALIDATORS.put(DOZE_PULSE_ON_DOUBLE_TAP, DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR); + VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR); + VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, + AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, + ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR); + VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR); + VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR); + VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR); + VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR); + VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR); + VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_SLEEP, SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR); + VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR); + VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR); + VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS, + ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting + VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT, + ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR); //legacy restore setting + VALIDATORS.put(ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, + ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting + } + + /** + * Keys we no longer back up under the current schema, but want to continue to + * process when restoring historical backup datasets. + * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ public static final String[] LEGACY_RESTORE_SETTINGS = { ENABLED_NOTIFICATION_LISTENERS, ENABLED_NOTIFICATION_ASSISTANT, @@ -7796,12 +8240,16 @@ public final class Settings { */ public static final String AUTO_TIME = "auto_time"; + private static final Validator AUTO_TIME_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Value to specify if the user prefers the time zone * to be automatically fetched from the network (NITZ). 1=yes, 0=no */ public static final String AUTO_TIME_ZONE = "auto_time_zone"; + private static final Validator AUTO_TIME_ZONE_VALIDATOR = BOOLEAN_VALIDATOR; + /** * URI for the car dock "in" event sound. * @hide @@ -7832,6 +8280,8 @@ public final class Settings { */ public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled"; + private static final Validator DOCK_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether to play a sound for dock events, only when an accessibility service is on. * @hide @@ -7869,6 +8319,8 @@ public final class Settings { */ public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; + private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * URI for the "wireless charging started" sound. * @hide @@ -7882,6 +8334,8 @@ public final class Settings { */ public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled"; + private static final Validator CHARGING_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether we keep the device on while the device is plugged in. * Supported values are: @@ -7895,6 +8349,30 @@ public final class Settings { */ public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in"; + private static final Validator STAY_ON_WHILE_PLUGGED_IN_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + int val = Integer.parseInt(value); + return (val == 0) + || (val == BatteryManager.BATTERY_PLUGGED_AC) + || (val == BatteryManager.BATTERY_PLUGGED_USB) + || (val == BatteryManager.BATTERY_PLUGGED_WIRELESS) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_USB)) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_WIRELESS)) + || (val == (BatteryManager.BATTERY_PLUGGED_USB + | BatteryManager.BATTERY_PLUGGED_WIRELESS)) + || (val == (BatteryManager.BATTERY_PLUGGED_AC + | BatteryManager.BATTERY_PLUGGED_USB + | BatteryManager.BATTERY_PLUGGED_WIRELESS)); + } catch (NumberFormatException e) { + return false; + } + } + }; + /** * When the user has enable the option to have a "bug report" command * in the power menu. @@ -7902,6 +8380,8 @@ public final class Settings { */ public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu"; + private static final Validator BUGREPORT_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Whether ADB is enabled. */ @@ -7925,6 +8405,8 @@ public final class Settings { */ public static final String BLUETOOTH_ON = "bluetooth_on"; + private static final Validator BLUETOOTH_ON_VALIDATOR = BOOLEAN_VALIDATOR; + /** * CDMA Cell Broadcast SMS * 0 = CDMA Cell Broadcast SMS disabled @@ -8543,6 +9025,8 @@ public final class Settings { */ public static final String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled"; + private static final Validator USB_MASS_STORAGE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * If this setting is set (to anything), then all references * to Gmail on the device must change to Google Mail. @@ -8679,6 +9163,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on"; + private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR = + BOOLEAN_VALIDATOR; + /** * {@hide} */ @@ -8692,6 +9179,9 @@ public final class Settings { public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay"; + private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * 802.11 country code in ISO 3166 format * @hide @@ -8721,6 +9211,9 @@ public final class Settings { */ public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept"; + private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Whether the Wi-Fi should be on. Only the Wi-Fi service should touch this. */ @@ -8741,6 +9234,8 @@ public final class Settings { */ public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled"; + private static final Validator SOFT_AP_TIMEOUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Value to specify if Wi-Fi Wakeup feature is enabled. * @@ -8750,6 +9245,8 @@ public final class Settings { @SystemApi public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled"; + private static final Validator WIFI_WAKEUP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * Value to specify if Wi-Fi Wakeup is available. * @@ -8796,6 +9293,9 @@ public final class Settings { public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled"; + private static final Validator NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "1"}); + /** * Which package name to use for network recommendations. If null, network recommendations * will neither be requested nor accepted. @@ -8819,6 +9319,13 @@ public final class Settings { @TestApi public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; + private static final Validator USE_OPEN_WIFI_PACKAGE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + return (value == null) || PACKAGE_NAME_VALIDATOR.validate(value); + } + }; + /** * The number of milliseconds the {@link com.android.server.NetworkScoreService} * will give a recommendation request to complete before returning a default response. @@ -8898,6 +9405,9 @@ public final class Settings { public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED = "wifi_watchdog_poor_network_test_enabled"; + private static final Validator WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED_VALIDATOR = + ANY_STRING_VALIDATOR; + /** * Setting to turn on suspend optimizations at screen off on Wi-Fi. Enabled by default and * needs to be set to 0 to disable it. @@ -9433,11 +9943,16 @@ public final class Settings { * @hide */ public static final String PRIVATE_DNS_MODE = "private_dns_mode"; + + private static final Validator PRIVATE_DNS_MODE_VALIDATOR = ANY_STRING_VALIDATOR; + /** * @hide */ public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier"; + private static final Validator PRIVATE_DNS_SPECIFIER_VALIDATOR = ANY_STRING_VALIDATOR; + /** {@hide} */ public static final String BLUETOOTH_HEADSET_PRIORITY_PREFIX = "bluetooth_headset_priority_"; @@ -9664,6 +10179,14 @@ public final class Settings { public static final String SYS_VDSO = "sys_vdso"; /** + * An integer to reduce the FPS by this factor. Only for experiments. Need to reboot the + * device for this setting to take full effect. + * + * @hide + */ + public static final String FPS_DEVISOR = "fps_divisor"; + + /** * App standby (app idle) specific settings. * This is encoded as a key=value list, separated by commas. Ex: * <p> @@ -10011,6 +10534,9 @@ public final class Settings { */ public static final String EMERGENCY_TONE = "emergency_tone"; + private static final Validator EMERGENCY_TONE_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"}); + /** * CDMA only settings * Whether the auto retry is enabled. The value is @@ -10019,6 +10545,8 @@ public final class Settings { */ public static final String CALL_AUTO_RETRY = "call_auto_retry"; + private static final Validator CALL_AUTO_RETRY_VALIDATOR = BOOLEAN_VALIDATOR; + /** * A setting that can be read whether the emergency affordance is currently needed. * The value is a boolean (1 or 0). @@ -10088,6 +10616,9 @@ public final class Settings { */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; + private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR = + new SettingsValidators.InclusiveIntegerRangeValidator(0, 99); + /** * If not 0, the activity manager will aggressively finish activities and * processes as soon as they are no longer needed. If 0, the normal @@ -10103,6 +10634,8 @@ public final class Settings { */ public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled"; + private static final Validator DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** * The surround sound formats AC3, DTS or IEC61937 are * available for use if they are detected. @@ -10149,6 +10682,9 @@ public final class Settings { */ public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output"; + private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"}); + /** * Persisted safe headphone volume management state by AudioService * @hide @@ -10689,6 +11225,41 @@ public final class Settings { }; /** + * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ + public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); + static { + VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR); + VALIDATORS.put(STAY_ON_WHILE_PLUGGED_IN, STAY_ON_WHILE_PLUGGED_IN_VALIDATOR); + VALIDATORS.put(AUTO_TIME, AUTO_TIME_VALIDATOR); + VALIDATORS.put(AUTO_TIME_ZONE, AUTO_TIME_ZONE_VALIDATOR); + VALIDATORS.put(POWER_SOUNDS_ENABLED, POWER_SOUNDS_ENABLED_VALIDATOR); + VALIDATORS.put(DOCK_SOUNDS_ENABLED, DOCK_SOUNDS_ENABLED_VALIDATOR); + VALIDATORS.put(CHARGING_SOUNDS_ENABLED, CHARGING_SOUNDS_ENABLED_VALIDATOR); + VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR); + VALIDATORS.put(NETWORK_RECOMMENDATIONS_ENABLED, + NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR); + VALIDATORS.put(WIFI_WAKEUP_ENABLED, WIFI_WAKEUP_ENABLED_VALIDATOR); + VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR); + VALIDATORS.put(USE_OPEN_WIFI_PACKAGE, USE_OPEN_WIFI_PACKAGE_VALIDATOR); + VALIDATORS.put(WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, + WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED_VALIDATOR); + VALIDATORS.put(EMERGENCY_TONE, EMERGENCY_TONE_VALIDATOR); + VALIDATORS.put(CALL_AUTO_RETRY, CALL_AUTO_RETRY_VALIDATOR); + VALIDATORS.put(DOCK_AUDIO_MEDIA_ENABLED, DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR); + VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR); + VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR); + VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR); + VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR); + VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR); + VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR); + } + + /** * Global settings that shouldn't be persisted. * * @hide @@ -10697,7 +11268,15 @@ public final class Settings { LOCATION_GLOBAL_KILL_SWITCH, }; - /** @hide */ + /** + * Keys we no longer back up under the current schema, but want to continue to + * process when restoring historical backup datasets. + * + * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator, + * otherwise they won't be restored. + * + * @hide + */ public static final String[] LEGACY_RESTORE_SETTINGS = { }; @@ -11309,6 +11888,20 @@ public final class Settings { */ public static final String ZRAM_ENABLED = "zram_enabled"; + + /** + * Whether smart replies in notifications are enabled. + * @hide + */ + public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS = + "enable_smart_replies_in_notifications"; + + /** + * If nonzero, crashes in foreground processes will bring up a dialog. + * Otherwise, the process will be silently killed. + * @hide + */ + public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog"; } /** diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java new file mode 100644 index 000000000000..84c9e8867c44 --- /dev/null +++ b/core/java/android/provider/SettingsValidators.java @@ -0,0 +1,249 @@ +/* + * 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.provider; + +import android.content.ComponentName; +import android.net.Uri; + +import com.android.internal.util.ArrayUtils; + +import java.util.Locale; + +/** + * This class provides both interface for validation and common validators + * used to ensure Settings have meaningful values. + * + * @hide + */ +public class SettingsValidators { + + public static final Validator BOOLEAN_VALIDATOR = + new DiscreteValueValidator(new String[] {"0", "1"}); + + public static final Validator ANY_STRING_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + return true; + } + }; + + public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + return Integer.parseInt(value) >= 0; + } catch (NumberFormatException e) { + return false; + } + } + }; + + public static final Validator ANY_INTEGER_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + Integer.parseInt(value); + return true; + } catch (NumberFormatException e) { + return false; + } + } + }; + + public static final Validator URI_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + Uri.decode(value); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + }; + + public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + return ComponentName.unflattenFromString(value) != null; + } + }; + + public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + return value != null && isStringPackageName(value); + } + + private boolean isStringPackageName(String value) { + // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers, + // and underscores ('_'). However, individual package name parts may only + // start with letters. + // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package) + if (value == null) { + return false; + } + String[] subparts = value.split("\\."); + boolean isValidPackageName = true; + for (String subpart : subparts) { + isValidPackageName |= isSubpartValidForPackageName(subpart); + if (!isValidPackageName) break; + } + return isValidPackageName; + } + + private boolean isSubpartValidForPackageName(String subpart) { + if (subpart.length() == 0) return false; + boolean isValidSubpart = Character.isLetter(subpart.charAt(0)); + for (int i = 1; i < subpart.length(); i++) { + isValidSubpart |= (Character.isLetterOrDigit(subpart.charAt(i)) + || (subpart.charAt(i) == '_')); + if (!isValidSubpart) break; + } + return isValidSubpart; + } + }; + + public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() { + private static final int MAX_IPV6_LENGTH = 45; + + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + return value.length() <= MAX_IPV6_LENGTH; + } + }; + + public static final Validator LOCALE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + Locale[] validLocales = Locale.getAvailableLocales(); + for (Locale locale : validLocales) { + if (value.equals(locale.toString())) { + return true; + } + } + return false; + } + }; + + public interface Validator { + boolean validate(String value); + } + + public static final class DiscreteValueValidator implements Validator { + private final String[] mValues; + + public DiscreteValueValidator(String[] values) { + mValues = values; + } + + @Override + public boolean validate(String value) { + return ArrayUtils.contains(mValues, value); + } + } + + public static final class InclusiveIntegerRangeValidator implements Validator { + private final int mMin; + private final int mMax; + + public InclusiveIntegerRangeValidator(int min, int max) { + mMin = min; + mMax = max; + } + + @Override + public boolean validate(String value) { + try { + final int intValue = Integer.parseInt(value); + return intValue >= mMin && intValue <= mMax; + } catch (NumberFormatException e) { + return false; + } + } + } + + public static final class InclusiveFloatRangeValidator implements Validator { + private final float mMin; + private final float mMax; + + public InclusiveFloatRangeValidator(float min, float max) { + mMin = min; + mMax = max; + } + + @Override + public boolean validate(String value) { + try { + final float floatValue = Float.parseFloat(value); + return floatValue >= mMin && floatValue <= mMax; + } catch (NumberFormatException e) { + return false; + } + } + } + + public static final class ComponentNameListValidator implements Validator { + private final String mSeparator; + + public ComponentNameListValidator(String separator) { + mSeparator = separator; + } + + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] elements = value.split(mSeparator); + for (String element : elements) { + if (!COMPONENT_NAME_VALIDATOR.validate(element)) { + return false; + } + } + return true; + } + } + + public static final class PackageNameListValidator implements Validator { + private final String mSeparator; + + public PackageNameListValidator(String separator) { + mSeparator = separator; + } + + @Override + public boolean validate(String value) { + if (value == null) { + return false; + } + String[] elements = value.split(mSeparator); + for (String element : elements) { + if (!PACKAGE_NAME_VALIDATOR.validate(element)) { + return false; + } + } + return true; + } + } +} diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index 57477f580a42..b5496e4a54dc 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -51,7 +51,6 @@ interface IKeystoreService { String grant(String name, int granteeUid); int ungrant(String name, int granteeUid); long getmtime(String name, int uid); - int duplicate(String srcKey, int srcUid, String destKey, int destUid); int is_hardware_backed(String string); int clear_uid(long uid); diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index f409e5b74d23..72afbb8414f6 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -73,6 +73,7 @@ public final class KeymasterDefs { public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504; public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505; public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; + public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; diff --git a/core/java/android/security/keystore/BadCertificateFormatException.java b/core/java/android/security/keystore/BadCertificateFormatException.java new file mode 100644 index 000000000000..ddc7bd2366ac --- /dev/null +++ b/core/java/android/security/keystore/BadCertificateFormatException.java @@ -0,0 +1,28 @@ +/* + * 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.security.keystore; + +/** + * Error thrown when the recovery agent supplies an invalid X509 certificate. + * + * @hide + */ +public class BadCertificateFormatException extends RecoveryControllerException { + public BadCertificateFormatException(String msg) { + super(msg); + } +} diff --git a/core/java/android/security/keystore/DecryptionFailedException.java b/core/java/android/security/keystore/DecryptionFailedException.java new file mode 100644 index 000000000000..945fcf6f88f2 --- /dev/null +++ b/core/java/android/security/keystore/DecryptionFailedException.java @@ -0,0 +1,30 @@ +/* + * 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.security.keystore; + +/** + * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key, + * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc. + * + * @hide + */ +public class DecryptionFailedException extends RecoveryControllerException { + + public DecryptionFailedException(String msg) { + super(msg); + } +} diff --git a/core/java/android/security/keystore/InternalRecoveryServiceException.java b/core/java/android/security/keystore/InternalRecoveryServiceException.java new file mode 100644 index 000000000000..85829bed9191 --- /dev/null +++ b/core/java/android/security/keystore/InternalRecoveryServiceException.java @@ -0,0 +1,35 @@ +/* + * 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.security.keystore; + +/** + * An error thrown when something went wrong internally in the recovery service. + * + * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the + * caller having performed some kind of illegal action. + * + * @hide + */ +public class InternalRecoveryServiceException extends RecoveryControllerException { + public InternalRecoveryServiceException(String msg) { + super(msg); + } + + public InternalRecoveryServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/java/android/security/keystore/KeychainProtectionParameter.aidl b/core/java/android/security/keystore/KeychainProtectionParams.aidl index 1e2c365d4e69..0341223b081a 100644 --- a/core/java/android/security/keystore/KeychainProtectionParameter.aidl +++ b/core/java/android/security/keystore/KeychainProtectionParams.aidl @@ -17,4 +17,4 @@ package android.security.keystore; /* @hide */ -parcelable KeychainProtectionParameter; +parcelable KeychainProtectionParams; diff --git a/core/java/android/security/keystore/KeychainProtectionParameter.java b/core/java/android/security/keystore/KeychainProtectionParams.java index 2319ef5e3129..a3cd431b983d 100644 --- a/core/java/android/security/keystore/KeychainProtectionParameter.java +++ b/core/java/android/security/keystore/KeychainProtectionParams.java @@ -47,7 +47,7 @@ import java.util.Arrays; * * @hide */ -public final class KeychainProtectionParameter implements Parcelable { +public final class KeychainProtectionParams implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD}) @@ -102,7 +102,7 @@ public final class KeychainProtectionParameter implements Parcelable { * @link {#clearSecret} to overwrite its value in memory. * @hide */ - public KeychainProtectionParameter(@UserSecretType int userSecretType, + public KeychainProtectionParams(@UserSecretType int userSecretType, @LockScreenUiFormat int lockScreenUiFormat, @NonNull KeyDerivationParams keyDerivationParams, @NonNull byte[] secret) { @@ -112,7 +112,7 @@ public final class KeychainProtectionParameter implements Parcelable { mSecret = Preconditions.checkNotNull(secret); } - private KeychainProtectionParameter() { + private KeychainProtectionParams() { } @@ -155,10 +155,10 @@ public final class KeychainProtectionParameter implements Parcelable { } /** - * Builder for creating {@link KeychainProtectionParameter}. + * Builder for creating {@link KeychainProtectionParams}. */ public static class Builder { - private KeychainProtectionParameter mInstance = new KeychainProtectionParameter(); + private KeychainProtectionParams mInstance = new KeychainProtectionParams(); /** * Sets user secret type. @@ -212,14 +212,14 @@ public final class KeychainProtectionParameter implements Parcelable { /** - * Creates a new {@link KeychainProtectionParameter} instance. + * Creates a new {@link KeychainProtectionParams} instance. * The instance will include default values, if {@link setSecret} * or {@link setUserSecretType} were not called. * * @return new instance * @throws NullPointerException if some required fields were not set. */ - @NonNull public KeychainProtectionParameter build() { + @NonNull public KeychainProtectionParams build() { if (mInstance.mUserSecretType == null) { mInstance.mUserSecretType = TYPE_LOCKSCREEN; } @@ -249,14 +249,14 @@ public final class KeychainProtectionParameter implements Parcelable { Arrays.fill(mSecret, (byte) 0); } - public static final Parcelable.Creator<KeychainProtectionParameter> CREATOR = - new Parcelable.Creator<KeychainProtectionParameter>() { - public KeychainProtectionParameter createFromParcel(Parcel in) { - return new KeychainProtectionParameter(in); + public static final Parcelable.Creator<KeychainProtectionParams> CREATOR = + new Parcelable.Creator<KeychainProtectionParams>() { + public KeychainProtectionParams createFromParcel(Parcel in) { + return new KeychainProtectionParams(in); } - public KeychainProtectionParameter[] newArray(int length) { - return new KeychainProtectionParameter[length]; + public KeychainProtectionParams[] newArray(int length) { + return new KeychainProtectionParams[length]; } }; @@ -274,7 +274,7 @@ public final class KeychainProtectionParameter implements Parcelable { /** * @hide */ - protected KeychainProtectionParameter(Parcel in) { + protected KeychainProtectionParams(Parcel in) { mUserSecretType = in.readInt(); mLockScreenUiFormat = in.readInt(); mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR); diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java index 71a808a41f57..e03dd4a62ca0 100644 --- a/core/java/android/security/keystore/KeychainSnapshot.java +++ b/core/java/android/security/keystore/KeychainSnapshot.java @@ -43,8 +43,15 @@ import java.util.List; * @hide */ public final class KeychainSnapshot implements Parcelable { + private static final int DEFAULT_MAX_ATTEMPTS = 10; + private static final long DEFAULT_COUNTER_ID = 1L; + private int mSnapshotVersion; - private List<KeychainProtectionParameter> mKeychainProtectionParams; + private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS; + private long mCounterId = DEFAULT_COUNTER_ID; + private byte[] mServerParams; + private byte[] mPublicKey; + private List<KeychainProtectionParams> mKeychainProtectionParams; private List<WrappedApplicationKey> mEntryRecoveryData; private byte[] mEncryptedRecoveryKeyBlob; @@ -54,7 +61,7 @@ public final class KeychainSnapshot implements Parcelable { */ public KeychainSnapshot( int snapshotVersion, - @NonNull List<KeychainProtectionParameter> keychainProtectionParams, + @NonNull List<KeychainProtectionParams> keychainProtectionParams, @NonNull List<WrappedApplicationKey> wrappedApplicationKeys, @NonNull byte[] encryptedRecoveryKeyBlob) { mSnapshotVersion = snapshotVersion; @@ -79,9 +86,40 @@ public final class KeychainSnapshot implements Parcelable { } /** + * Number of user secret guesses allowed during Keychain recovery. + */ + public int getMaxAttempts() { + return mMaxAttempts; + } + + /** + * CounterId which is rotated together with user secret. + */ + public long getCounterId() { + return mCounterId; + } + + /** + * Server parameters. + */ + public @NonNull byte[] getServerParams() { + return mServerParams; + } + + /** + * Public key used to encrypt {@code encryptedRecoveryKeyBlob}. + * + * See implementation for binary key format + */ + // TODO: document key format. + public @NonNull byte[] getTrustedHardwarePublicKey() { + return mPublicKey; + } + + /** * UI and key derivation parameters. Note that combination of secrets may be used. */ - public @NonNull List<KeychainProtectionParameter> getKeychainProtectionParams() { + public @NonNull List<KeychainProtectionParams> getKeychainProtectionParams() { return mKeychainProtectionParams; } @@ -129,13 +167,57 @@ public final class KeychainSnapshot implements Parcelable { } /** + * Sets the number of user secret guesses allowed during Keychain recovery. + * + * @param maxAttempts The maximum number of guesses. + * @return This builder. + */ + public Builder setMaxAttempts(int maxAttempts) { + mInstance.mMaxAttempts = maxAttempts; + return this; + } + + /** + * Sets counter id. + * + * @param counterId The counter id. + * @return This builder. + */ + public Builder setCounterId(long counterId) { + mInstance.mCounterId = counterId; + return this; + } + + /** + * Sets server parameters. + * + * @param serverParams The server parameters + * @return This builder. + */ + public Builder setServerParams(byte[] serverParams) { + mInstance.mServerParams = serverParams; + return this; + } + + /** + * Sets public key used to encrypt recovery blob. + * + * @param publicKey The public key + * @return This builder. + */ + public Builder setTrustedHardwarePublicKey(byte[] publicKey) { + mInstance.mPublicKey = publicKey; + return this; + } + + /** * Sets UI and key derivation parameters * * @param recoveryMetadata The UI and key derivation parameters * @return This builder. */ public Builder setKeychainProtectionParams( - @NonNull List<KeychainProtectionParameter> recoveryMetadata) { + @NonNull List<KeychainProtectionParams> recoveryMetadata) { mInstance.mKeychainProtectionParams = recoveryMetadata; return this; } @@ -175,6 +257,8 @@ public final class KeychainSnapshot implements Parcelable { Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData, "entryRecoveryData"); Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob); + Preconditions.checkNotNull(mInstance.mServerParams); + Preconditions.checkNotNull(mInstance.mPublicKey); return mInstance; } } @@ -195,7 +279,7 @@ public final class KeychainSnapshot implements Parcelable { */ protected KeychainSnapshot(Parcel in) { mSnapshotVersion = in.readInt(); - mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParameter.CREATOR); + mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParams.CREATOR); mEncryptedRecoveryKeyBlob = in.createByteArray(); mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR); } diff --git a/core/java/android/security/keystore/LockScreenRequiredException.java b/core/java/android/security/keystore/LockScreenRequiredException.java new file mode 100644 index 000000000000..b07fb9cdd002 --- /dev/null +++ b/core/java/android/security/keystore/LockScreenRequiredException.java @@ -0,0 +1,30 @@ +/* + * 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.security.keystore; + +/** + * Error thrown when trying to generate keys for a profile that has no lock screen set. + * + * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot. + * + * @hide + */ +public class LockScreenRequiredException extends RecoveryControllerException { + public LockScreenRequiredException(String msg) { + super(msg); + } +} diff --git a/core/java/android/security/keystore/RecoveryClaim.java b/core/java/android/security/keystore/RecoveryClaim.java new file mode 100644 index 000000000000..6f566af1dc7d --- /dev/null +++ b/core/java/android/security/keystore/RecoveryClaim.java @@ -0,0 +1,54 @@ +/* + * 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.security.keystore; + +/** + * An attempt to recover a keychain protected by remote secure hardware. + * + * @hide + */ +public class RecoveryClaim { + + private final RecoverySession mRecoverySession; + private final byte[] mClaimBytes; + + RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) { + mRecoverySession = recoverySession; + mClaimBytes = claimBytes; + } + + /** + * Returns the session associated with the recovery attempt. This is used to match the symmetric + * key, which remains internal to the framework, for decrypting the claim response. + * + * @return The session data. + */ + public RecoverySession getRecoverySession() { + return mRecoverySession; + } + + /** + * Returns the encrypted claim's bytes. + * + * <p>This should be sent by the recovery agent to the remote secure hardware, which will use + * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key + * to the device. + */ + public byte[] getClaimBytes() { + return mClaimBytes; + } +} diff --git a/core/java/android/security/keystore/RecoveryManager.java b/core/java/android/security/keystore/RecoveryController.java index bddf3e849182..87283cbd75ab 100644 --- a/core/java/android/security/keystore/RecoveryManager.java +++ b/core/java/android/security/keystore/RecoveryController.java @@ -23,6 +23,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.util.Log; import com.android.internal.widget.ILockSettings; @@ -30,12 +31,26 @@ import java.util.List; import java.util.Map; /** - * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and - * recovered later. + * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by + * other Android devices belonging to the user. The exported keychain is protected by the user's + * lock screen. + * + * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible + * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force + * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10). + * After that number of incorrect guesses, the trusted hardware no longer allows access to the + * key chain. + * + * <p>For now only the recovery agent itself is able to create keys, so it is expected that the + * recovery agent is itself the system app. + * + * <p>A recovery agent requires the privileged permission + * {@code android.Manifest.permission#RECOVER_KEYSTORE}. * * @hide */ -public class RecoveryManager { +public class RecoveryController { + private static final String TAG = "RecoveryController"; /** Key has been successfully synced. */ public static final int RECOVERY_STATUS_SYNCED = 0; @@ -46,48 +61,97 @@ public class RecoveryManager { /** Key cannot be synced. */ public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3; + /** + * Failed because no snapshot is yet pending to be synced for the user. + * + * @hide + */ + public static final int ERROR_NO_SNAPSHOT_PENDING = 21; + + /** + * Failed due to an error internal to the recovery service. This is unexpected and indicates + * either a problem with the logic in the service, or a problem with a dependency of the + * service (such as AndroidKeyStore). + * + * @hide + */ + public static final int ERROR_SERVICE_INTERNAL_ERROR = 22; + + /** + * Failed because the user does not have a lock screen set. + * + * @hide + */ + public static final int ERROR_INSECURE_USER = 23; + + /** + * Error thrown when attempting to use a recovery session that has since been closed. + * + * @hide + */ + public static final int ERROR_SESSION_EXPIRED = 24; + + /** + * Failed because the provided certificate was not a valid X509 certificate. + * + * @hide + */ + public static final int ERROR_BAD_CERTIFICATE_FORMAT = 25; + + /** + * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong, + * the data has become corrupted, the data has been tampered with, etc. + * + * @hide + */ + public static final int ERROR_DECRYPTION_FAILED = 26; + + private final ILockSettings mBinder; - private RecoveryManager(ILockSettings binder) { + private RecoveryController(ILockSettings binder) { mBinder = binder; } /** * Gets a new instance of the class. */ - public static RecoveryManager getInstance() { + public static RecoveryController getInstance() { ILockSettings lockSettings = ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")); - return new RecoveryManager(lockSettings); + return new RecoveryController(lockSettings); } /** - * Initializes key recovery service for the calling application. RecoveryManager + * Initializes key recovery service for the calling application. RecoveryController * randomly chooses one of the keys from the list and keeps it to use for future key export * operations. Collection of all keys in the list must be signed by the provided {@code * rootCertificateAlias}, which must also be present in the list of root certificates - * preinstalled on the device. The random selection allows RecoveryManager to select + * preinstalled on the device. The random selection allows RecoveryController to select * which of a set of remote recovery service devices will be used. * - * <p>In addition, RecoveryManager enforces a delay of three months between + * <p>In addition, RecoveryController enforces a delay of three months between * consecutive initialization attempts, to limit the ability of an attacker to often switch * remote recovery devices and significantly increase number of recovery attempts. * * @param rootCertificateAlias alias of a root certificate preinstalled on the device * @param signedPublicKeyList binary blob a list of X509 certificates and signature - * @throws RecoveryManagerException if signature is invalid, or key rotation was rate - * limited. - * @hide + * @throws BadCertificateFormatException if the {@code signedPublicKeyList} is in a bad format. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ public void initRecoveryService( @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList) - throws RecoveryManagerException { + throws BadCertificateFormatException, InternalRecoveryServiceException { try { mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) { + throw new BadCertificateFormatException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); } } @@ -97,17 +161,20 @@ public class RecoveryManager { * * @param account specific to Recovery agent. * @return Data necessary to recover keystore. - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - @NonNull public KeychainSnapshot getRecoveryData(@NonNull byte[] account) - throws RecoveryManagerException { + public @NonNull KeychainSnapshot getRecoveryData(@NonNull byte[] account) + throws InternalRecoveryServiceException { try { - KeychainSnapshot keychainSnapshot = mBinder.getRecoveryData(account); - return keychainSnapshot; + return mBinder.getRecoveryData(account); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) { + return null; + } + throw wrapUnexpectedServiceSpecificException(e); } } @@ -118,16 +185,17 @@ public class RecoveryManager { * * @param intent triggered when new snapshot is available. Unregisters listener if the value is * {@code null}. - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent) - throws RecoveryManagerException { + throws InternalRecoveryServiceException { try { mBinder.setSnapshotCreatedPendingIntent(intent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -137,10 +205,11 @@ public class RecoveryManager { * * @return Map from recovery agent accounts to snapshot versions. * @see KeychainSnapshot#getSnapshotVersion - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions() - throws RecoveryManagerException { + throws InternalRecoveryServiceException { try { // IPC doesn't support generic Maps. @SuppressWarnings("unchecked") @@ -150,7 +219,7 @@ public class RecoveryManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -161,16 +230,16 @@ public class RecoveryManager { * * @param serverParams included in recovery key blob. * @see #getRecoveryData - * @throws RecoveryManagerException If parameters rotation is rate limited. - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - public void setServerParams(byte[] serverParams) throws RecoveryManagerException { + public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException { try { mBinder.setServerParams(serverParams); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -183,16 +252,18 @@ public class RecoveryManager { * @param aliases List of application-specific key aliases. If the array is empty, updates the * status for all existing recoverable keys. * @param status Status specific to recovery agent. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ public void setRecoveryStatus( @NonNull String packageName, @Nullable String[] aliases, int status) - throws NameNotFoundException, RecoveryManagerException { + throws NameNotFoundException, InternalRecoveryServiceException { try { mBinder.setRecoveryStatus(packageName, aliases, status); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -209,10 +280,10 @@ public class RecoveryManager { * * @return {@code Map} from KeyStore alias to recovery status. * @see #setRecoveryStatus - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - public Map<String, Integer> getRecoveryStatus() - throws RecoveryManagerException { + public Map<String, Integer> getRecoveryStatus() throws InternalRecoveryServiceException { try { // IPC doesn't support generic Maps. @SuppressWarnings("unchecked") @@ -222,7 +293,7 @@ public class RecoveryManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -230,18 +301,20 @@ public class RecoveryManager { * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them * is necessary to recover data. * - * @param secretTypes {@link KeychainProtectionParameter#TYPE_LOCKSCREEN} or {@link - * KeychainProtectionParameter#TYPE_CUSTOM_PASSWORD} + * @param secretTypes {@link KeychainProtectionParams#TYPE_LOCKSCREEN} or {@link + * KeychainProtectionParams#TYPE_CUSTOM_PASSWORD} + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ public void setRecoverySecretTypes( - @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes) - throws RecoveryManagerException { + @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes) + throws InternalRecoveryServiceException { try { mBinder.setRecoverySecretTypes(secretTypes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -251,15 +324,17 @@ public class RecoveryManager { * * @return list of recovery secret types * @see KeychainSnapshot + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - @NonNull public @KeychainProtectionParameter.UserSecretType int[] getRecoverySecretTypes() - throws RecoveryManagerException { + public @NonNull @KeychainProtectionParams.UserSecretType int[] getRecoverySecretTypes() + throws InternalRecoveryServiceException { try { return mBinder.getRecoverySecretTypes(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -269,38 +344,40 @@ public class RecoveryManager { * called. * * @return list of recovery secret types - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ @NonNull - public @KeychainProtectionParameter.UserSecretType int[] getPendingRecoverySecretTypes() - throws RecoveryManagerException { + public @KeychainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes() + throws InternalRecoveryServiceException { try { return mBinder.getPendingRecoverySecretTypes(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } /** * Method notifies KeyStore that a user-generated secret is available. This method generates a * symmetric session key which a trusted remote device can use to return a recovery key. Caller - * should use {@link KeychainProtectionParameter#clearSecret} to override the secret value in + * should use {@link KeychainProtectionParams#clearSecret} to override the secret value in * memory. * * @param recoverySecret user generated secret together with parameters necessary to regenerate * it on a new device. - * @hide + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret) - throws RecoveryManagerException { + public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret) + throws InternalRecoveryServiceException { try { mBinder.recoverySecretAvailable(recoverySecret); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); } } @@ -309,7 +386,6 @@ public class RecoveryManager { * The method generates symmetric key for a session, which trusted remote device can use to * return recovery key. * - * @param sessionId ID for recovery session. * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key * used to create the recovery blob on the source device. * Keystore will verify the certificate using root of trust. @@ -318,56 +394,88 @@ public class RecoveryManager { * @param vaultChallenge Data passed from server for this recovery session and used to prevent * replay attacks * @param secrets Secrets provided by user, the method only uses type and secret fields. - * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains - * a proof of user secrets, session symmetric key and parameters necessary to identify the - * counter with the number of failed recovery attempts. + * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is + * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric + * key and parameters necessary to identify the counter with the number of failed recovery + * attempts. + * @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect + * format. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - public @NonNull byte[] startRecoverySession( - @NonNull String sessionId, + @NonNull public RecoveryClaim startRecoverySession( @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, - @NonNull List<KeychainProtectionParameter> secrets) - throws RecoveryManagerException { + @NonNull List<KeychainProtectionParams> secrets) + throws BadCertificateFormatException, InternalRecoveryServiceException { try { + RecoverySession recoverySession = RecoverySession.newInstance(this); byte[] recoveryClaim = mBinder.startRecoverySession( - sessionId, + recoverySession.getSessionId(), verifierPublicKey, vaultParams, vaultChallenge, secrets); - return recoveryClaim; + return new RecoveryClaim(recoverySession, recoveryClaim); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) { + throw new BadCertificateFormatException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); } } /** * Imports keys. * - * @param sessionId Id for recovery session, same as in - * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. + * @param session Related recovery session, as originally created by invoking + * {@link #startRecoverySession(byte[], byte[], byte[], List)}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link * WrappedApplicationKey}. Caller is responsibility to perform certificates check. * @return Map from alias to raw key material. + * @throws SessionExpiredException if {@code session} has since been closed. + * @throws DecryptionFailedException if unable to decrypt the snapshot. + * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. */ public Map<String, byte[]> recoverKeys( - @NonNull String sessionId, + @NonNull RecoverySession session, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) - throws RecoveryManagerException { + throws SessionExpiredException, DecryptionFailedException, + InternalRecoveryServiceException { try { return (Map<String, byte[]>) mBinder.recoverKeys( - sessionId, recoveryKeyBlob, applicationKeys); + session.getSessionId(), recoveryKeyBlob, applicationKeys); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + if (e.errorCode == ERROR_DECRYPTION_FAILED) { + throw new DecryptionFailedException(e.getMessage()); + } + if (e.errorCode == ERROR_SESSION_EXPIRED) { + throw new SessionExpiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); + } + } + + /** + * Deletes all data associated with {@code session}. Should not be invoked directly but via + * {@link RecoverySession#close()}. + * + * @hide + */ + void closeSession(RecoverySession session) { + try { + mBinder.closeSession(session.getSessionId()); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Unexpected error trying to close session", e); } } @@ -376,17 +484,23 @@ public class RecoveryManager { * raw material of the key. * * @param alias The key alias. - * @throws RecoveryManagerException if an error occurred generating and storing the - * key. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. + * @throws LockScreenRequiredException if the user has not set a lock screen. This is required + * to generate recoverable keys, as the snapshots are encrypted using a key derived from the + * lock screen. */ public byte[] generateAndStoreKey(@NonNull String alias) - throws RecoveryManagerException { + throws InternalRecoveryServiceException, LockScreenRequiredException { try { return mBinder.generateAndStoreKey(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + if (e.errorCode == ERROR_INSECURE_USER) { + throw new LockScreenRequiredException(e.getMessage()); + } + throw wrapUnexpectedServiceSpecificException(e); } } @@ -394,14 +508,28 @@ public class RecoveryManager { * Removes a key called {@code alias} from the recoverable key store. * * @param alias The key alias. + * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery + * service. */ - public void removeKey(@NonNull String alias) throws RecoveryManagerException { + public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException { try { mBinder.removeKey(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { - throw RecoveryManagerException.fromServiceSpecificException(e); + throw wrapUnexpectedServiceSpecificException(e); + } + } + + private InternalRecoveryServiceException wrapUnexpectedServiceSpecificException( + ServiceSpecificException e) { + if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) { + return new InternalRecoveryServiceException(e.getMessage()); } + + // Should never happen. If it does, it's a bug, and we need to update how the method that + // called this throws its exceptions. + return new InternalRecoveryServiceException("Unexpected error code for method: " + + e.errorCode, e); } } diff --git a/core/java/android/security/keystore/RecoveryControllerException.java b/core/java/android/security/keystore/RecoveryControllerException.java new file mode 100644 index 000000000000..5b806b75ebab --- /dev/null +++ b/core/java/android/security/keystore/RecoveryControllerException.java @@ -0,0 +1,36 @@ +/* + * 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.security.keystore; + +import java.security.GeneralSecurityException; + +/** + * Base exception for errors thrown by {@link RecoveryController}. + * + * @hide + */ +public abstract class RecoveryControllerException extends GeneralSecurityException { + RecoveryControllerException() { } + + RecoveryControllerException(String msg) { + super(msg); + } + + public RecoveryControllerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/java/android/security/keystore/RecoveryManagerException.java b/core/java/android/security/keystore/RecoveryManagerException.java deleted file mode 100644 index 344718aa31d4..000000000000 --- a/core/java/android/security/keystore/RecoveryManagerException.java +++ /dev/null @@ -1,111 +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 android.security.keystore; - -import android.os.ServiceSpecificException; - -/** - * Exception thrown by {@link RecoveryManager} methods. - * - * @hide - */ -public class RecoveryManagerException extends Exception { - /** - * Failed because the loader has not been initialized with a recovery public key yet. - */ - public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20; - - /** - * Failed because no snapshot is yet pending to be synced for the user. - */ - public static final int ERROR_NO_SNAPSHOT_PENDING = 21; - - /** - * Failed due to an error internal to AndroidKeyStore. - */ - public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22; - - /** - * Failed because the user does not have a lock screen set. - */ - public static final int ERROR_INSECURE_USER = 24; - - /** - * Failed because of an internal database error. - */ - public static final int ERROR_DATABASE_ERROR = 25; - - /** - * Failed because the provided certificate was not a valid X509 certificate. - */ - public static final int ERROR_BAD_X509_CERTIFICATE = 26; - - /** - * Should never be thrown - some algorithm that all AOSP implementations must support is - * not available. - */ - public static final int ERROR_UNEXPECTED_MISSING_ALGORITHM = 27; - - /** - * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong, - * the data has become corrupted, the data has been tampered with, etc. - */ - public static final int ERROR_DECRYPTION_FAILED = 28; - - /** - * Rate limit is enforced to prevent using too many trusted remote devices, since each device - * can have its own number of user secret guesses allowed. - * - * @hide - */ - public static final int ERROR_RATE_LIMIT_EXCEEDED = 29; - - private int mErrorCode; - - /** - * Creates new {@link #RecoveryManagerException} instance from the error code. - * - * @param errorCode An error code, as listed at the top of this file. - * @param message The associated error message. - * @hide - */ - public static RecoveryManagerException fromErrorCode( - int errorCode, String message) { - return new RecoveryManagerException(errorCode, message); - } - /** - * Creates new {@link #RecoveryManagerException} from {@link - * ServiceSpecificException}. - * - * @param e exception thrown on service side. - * @hide - */ - static RecoveryManagerException fromServiceSpecificException( - ServiceSpecificException e) throws RecoveryManagerException { - throw RecoveryManagerException.fromErrorCode(e.errorCode, e.getMessage()); - } - - private RecoveryManagerException(int errorCode, String message) { - super(message); - mErrorCode = errorCode; - } - - /** Returns errorCode. */ - public int getErrorCode() { - return mErrorCode; - } -} diff --git a/core/java/android/security/keystore/RecoverySession.java b/core/java/android/security/keystore/RecoverySession.java new file mode 100644 index 000000000000..ae8d91af3230 --- /dev/null +++ b/core/java/android/security/keystore/RecoverySession.java @@ -0,0 +1,71 @@ +/* + * 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.security.keystore; + +import java.security.SecureRandom; + +/** + * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a + * recovery agent. + * + * @hide + */ +public class RecoverySession implements AutoCloseable { + + private static final int SESSION_ID_LENGTH_BYTES = 16; + + private final String mSessionId; + private final RecoveryController mRecoveryController; + + private RecoverySession(RecoveryController recoveryController, String sessionId) { + mRecoveryController = recoveryController; + mSessionId = sessionId; + } + + /** + * A new session, started by {@code recoveryManager}. + */ + static RecoverySession newInstance(RecoveryController recoveryController) { + return new RecoverySession(recoveryController, newSessionId()); + } + + /** + * Returns a new random session ID. + */ + private static String newSessionId() { + SecureRandom secureRandom = new SecureRandom(); + byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES]; + secureRandom.nextBytes(sessionId); + StringBuilder sb = new StringBuilder(); + for (byte b : sessionId) { + sb.append(Byte.toHexString(b, /*upperCase=*/ false)); + } + return sb.toString(); + } + + /** + * An internal session ID, used by the framework to match recovery claims to snapshot responses. + */ + String getSessionId() { + return mSessionId; + } + + @Override + public void close() { + mRecoveryController.closeSession(this); + } +} diff --git a/core/java/android/security/keystore/SessionExpiredException.java b/core/java/android/security/keystore/SessionExpiredException.java new file mode 100644 index 000000000000..f13e20602625 --- /dev/null +++ b/core/java/android/security/keystore/SessionExpiredException.java @@ -0,0 +1,28 @@ +/* + * 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.security.keystore; + +/** + * Error thrown when attempting to use a {@link RecoverySession} that has since expired. + * + * @hide + */ +public class SessionExpiredException extends RecoveryControllerException { + public SessionExpiredException(String msg) { + super(msg); + } +} diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java new file mode 100644 index 000000000000..4e4caf04579f --- /dev/null +++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java @@ -0,0 +1,219 @@ +/* + * 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.service.autofill; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.util.Log; +import android.view.autofill.AutofillValue; + +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; + +import java.util.Arrays; +import java.util.List; + +/** + * A service that calculates field classification scores. + * + * <p>A field classification score is a {@code float} representing how well an + * {@link AutofillValue} filled matches a expected value predicted by an autofill service + * —a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}. + * + * <p>The exact score depends on the algorithm used to calculate it— the service must provide + * at least one default algorithm (which is used when the algorithm is not specified or is invalid), + * but it could provide more (in which case the algorithm name should be specifiied by the caller + * when calculating the scores). + * + * {@hide} + */ +@SystemApi +public abstract class AutofillFieldClassificationService extends Service { + + private static final String TAG = "AutofillFieldClassificationService"; + + private static final int MSG_GET_SCORES = 1; + + /** + * The {@link Intent} action that must be declared as handled by a service + * in its manifest for the system to recognize it as a quota providing service. + */ + public static final String SERVICE_INTERFACE = + "android.service.autofill.AutofillFieldClassificationService"; + + /** + * Manifest metadata key for the resource string containing the name of the default field + * classification algorithm. + */ + public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = + "android.autofill.field_classification.default_algorithm"; + /** + * Manifest metadata key for the resource string array containing the names of all field + * classification algorithms provided by the service. + */ + public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = + "android.autofill.field_classification.available_algorithms"; + + + /** {@hide} **/ + public static final String EXTRA_SCORES = "scores"; + + private AutofillFieldClassificationServiceWrapper mWrapper; + + private final HandlerCaller.Callback mHandlerCallback = (msg) -> { + final int action = msg.what; + final Bundle data = new Bundle(); + final RemoteCallback callback; + switch (action) { + case MSG_GET_SCORES: + final SomeArgs args = (SomeArgs) msg.obj; + callback = (RemoteCallback) args.arg1; + final String algorithmName = (String) args.arg2; + final Bundle algorithmArgs = (Bundle) args.arg3; + @SuppressWarnings("unchecked") + final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4); + @SuppressWarnings("unchecked") + final String[] userDataValues = (String[]) args.arg5; + final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues, + Arrays.asList(userDataValues)); + data.putParcelable(EXTRA_SCORES, new Scores(scores)); + break; + default: + Log.w(TAG, "Handling unknown message: " + action); + return; + } + callback.sendResult(data); + }; + + private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), + mHandlerCallback, true); + + /** @hide */ + public AutofillFieldClassificationService() { + + } + + @Override + public void onCreate() { + super.onCreate(); + mWrapper = new AutofillFieldClassificationServiceWrapper(); + } + + @Override + public IBinder onBind(Intent intent) { + return mWrapper; + } + + /** + * Calculates field classification scores in a batch. + * + * <p>See {@link AutofillFieldClassificationService} for more info about field classification + * scores. + * + * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the + * default algorithm will be used instead. + * @param args optional arguments to be passed to the algorithm. + * @param actualValues values entered by the user. + * @param userDataValues values predicted from the user data. + * @return the calculated scores, with the first dimension representing actual values and the + * second dimension values from {@link UserData}. + * + * {@hide} + */ + @Nullable + @SystemApi + public float[][] onGetScores(@Nullable String algorithm, + @Nullable Bundle args, @NonNull List<AutofillValue> actualValues, + @NonNull List<String> userDataValues) { + throw new UnsupportedOperationException("Must be implemented by external service"); + } + + private final class AutofillFieldClassificationServiceWrapper + extends IAutofillFieldClassificationService.Stub { + @Override + public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs, + List<AutofillValue> actualValues, String[] userDataValues) + throws RemoteException { + // TODO(b/70939974): refactor to use PooledLambda + mHandlerCaller.obtainMessageOOOOO(MSG_GET_SCORES, callback, algorithmName, + algorithmArgs, actualValues, userDataValues).sendToTarget(); + } + } + + /** + * Helper class used to encapsulate a float[][] in a Parcelable. + * + * {@hide} + */ + public static final class Scores implements Parcelable { + public final float[][] scores; + + private Scores(Parcel parcel) { + final int size1 = parcel.readInt(); + final int size2 = parcel.readInt(); + scores = new float[size1][size2]; + for (int i = 0; i < size1; i++) { + for (int j = 0; j < size2; j++) { + scores[i][j] = parcel.readFloat(); + } + } + } + + private Scores(float[][] scores) { + this.scores = scores; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + int size1 = scores.length; + int size2 = scores[0].length; + parcel.writeInt(size1); + parcel.writeInt(size2); + for (int i = 0; i < size1; i++) { + for (int j = 0; j < size2; j++) { + parcel.writeFloat(scores[i][j]); + } + } + } + + public static final Creator<Scores> CREATOR = new Creator<Scores>() { + @Override + public Scores createFromParcel(Parcel parcel) { + return new Scores(parcel); + } + + @Override + public Scores[] newArray(int size) { + return new Scores[size]; + } + }; + } +} diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java index 2413e97ba837..f52ac8505289 100644 --- a/core/java/android/service/autofill/CharSequenceTransformation.java +++ b/core/java/android/service/autofill/CharSequenceTransformation.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; -import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.autofill.AutofillId; @@ -31,6 +30,8 @@ import android.widget.TextView; import com.android.internal.util.Preconditions; +import java.util.LinkedHashMap; +import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -62,7 +63,9 @@ import java.util.regex.Pattern; public final class CharSequenceTransformation extends InternalTransformation implements Transformation, Parcelable { private static final String TAG = "CharSequenceTransformation"; - @NonNull private final ArrayMap<AutofillId, Pair<Pattern, String>> mFields; + + // Must use LinkedHashMap to preserve insertion order. + @NonNull private final LinkedHashMap<AutofillId, Pair<Pattern, String>> mFields; private CharSequenceTransformation(Builder builder) { mFields = builder.mFields; @@ -76,9 +79,9 @@ public final class CharSequenceTransformation extends InternalTransformation imp final StringBuilder converted = new StringBuilder(); final int size = mFields.size(); if (sDebug) Log.d(TAG, size + " multiple fields on id " + childViewId); - for (int i = 0; i < size; i++) { - final AutofillId id = mFields.keyAt(i); - final Pair<Pattern, String> field = mFields.valueAt(i); + for (Entry<AutofillId, Pair<Pattern, String>> entry : mFields.entrySet()) { + final AutofillId id = entry.getKey(); + final Pair<Pattern, String> field = entry.getValue(); final String value = finder.findByAutofillId(id); if (value == null) { Log.w(TAG, "No value for id " + id); @@ -107,8 +110,10 @@ public final class CharSequenceTransformation extends InternalTransformation imp * Builder for {@link CharSequenceTransformation} objects. */ public static class Builder { - @NonNull private final ArrayMap<AutofillId, Pair<Pattern, String>> mFields = - new ArrayMap<>(); + + // Must use LinkedHashMap to preserve insertion order. + @NonNull private final LinkedHashMap<AutofillId, Pair<Pattern, String>> mFields = + new LinkedHashMap<>(); private boolean mDestroyed; /** @@ -186,12 +191,15 @@ public final class CharSequenceTransformation extends InternalTransformation imp final Pattern[] regexs = new Pattern[size]; final String[] substs = new String[size]; Pair<Pattern, String> pair; - for (int i = 0; i < size; i++) { - ids[i] = mFields.keyAt(i); - pair = mFields.valueAt(i); + int i = 0; + for (Entry<AutofillId, Pair<Pattern, String>> entry : mFields.entrySet()) { + ids[i] = entry.getKey(); + pair = entry.getValue(); regexs[i] = pair.first; substs[i] = pair.second; + i++; } + parcel.writeParcelableArray(ids, flags); parcel.writeSerializable(regexs); parcel.writeStringArray(substs); diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java index 536180335a83..cd1efd68df6f 100644 --- a/core/java/android/service/autofill/FieldClassification.java +++ b/core/java/android/service/autofill/FieldClassification.java @@ -105,21 +105,16 @@ public final class FieldClassification { /** * Represents the score of a {@link UserData} entry for the field. - * - * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and - * the entry is identified by {@link #getRemoteId()}. */ public static final class Match { private final String mRemoteId; private final float mScore; - private final String mAlgorithm; /** @hide */ - public Match(String remoteId, float score, String algorithm) { + public Match(String remoteId, float score) { mRemoteId = Preconditions.checkNotNull(remoteId); mScore = score; - mAlgorithm = algorithm; } /** @@ -150,38 +145,22 @@ public final class FieldClassification { return mScore; } - /** - * Gets the algorithm used to calculate this score. - * - * <p>Typically, this is either the algorithm set by - * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)}, - * or the - * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}. - */ - @NonNull - public String getAlgorithm() { - return mAlgorithm; - } - @Override public String toString() { if (!sDebug) return super.toString(); final StringBuilder string = new StringBuilder("Match: remoteId="); Helper.appendRedacted(string, mRemoteId); - return string.append(", score=").append(mScore) - .append(", algorithm=").append(mAlgorithm) - .toString(); + return string.append(", score=").append(mScore).toString(); } private void writeToParcel(@NonNull Parcel parcel) { parcel.writeString(mRemoteId); parcel.writeFloat(mScore); - parcel.writeString(mAlgorithm); } private static Match readFromParcel(@NonNull Parcel parcel) { - return new Match(parcel.readString(), parcel.readFloat(), parcel.readString()); + return new Match(parcel.readString(), parcel.readFloat()); } } } diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl new file mode 100644 index 000000000000..398557d5ad2e --- /dev/null +++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl @@ -0,0 +1,32 @@ +/* + * 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.service.autofill; + +import android.os.Bundle; +import android.os.RemoteCallback; +import android.view.autofill.AutofillValue; +import java.util.List; + +/** + * Service used to calculate match scores for Autofill Field Classification. + * + * @hide + */ +oneway interface IAutofillFieldClassificationService { + void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs, + in List<AutofillValue> actualValues, in String[] userDataValues); +} diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java index 2f9225acc520..9017848055e2 100644 --- a/core/java/android/service/autofill/UserData.java +++ b/core/java/android/service/autofill/UserData.java @@ -155,12 +155,9 @@ public final class UserData implements Parcelable { * <p>The currently available algorithms can be retrieve through * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}. * - * <p><b>Note: </b>The available algorithms in the Android System can change dinamically, - * so it's not guaranteed that the algorithm set here is the one that will be effectually - * used. If the algorithm set here is not available at runtime, the - * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead. - * You can verify which algorithm was used by calling - * {@link FieldClassification.Match#getAlgorithm()}. + * <p>If not set, the + * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is + * used instead. * * @param name name of the algorithm or {@code null} to used default. * @param args optional arguments to the algorithm. diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index c93e0365d58d..c7d4a4ac69aa 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -356,6 +356,7 @@ public class MeasuredParagraph { @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextDirectionHeuristic textDir, + boolean computeHyphenation, @Nullable MeasuredParagraph recycle) { final MeasuredParagraph mt = recycle == null ? obtain() : recycle; mt.resetAndAnalyzeBidi(text, start, end, textDir); @@ -365,7 +366,8 @@ public class MeasuredParagraph { long nativeBuilderPtr = nInitBuilder(); try { mt.bindNativeObject( - nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); + nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, + computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } @@ -394,7 +396,8 @@ public class MeasuredParagraph { mt.mSpanEndCache.append(spanEnd); } } - mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer)); + mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer, + computeHyphenation)); } finally { nFreeBuilder(nativeBuilderPtr); } @@ -668,7 +671,8 @@ public class MeasuredParagraph { @FloatRange(from = 0) float width); private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr, - @NonNull char[] text); + @NonNull char[] text, + boolean computeHyphenation); private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr); diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index 2c30360b81bc..b96b48954ee5 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -52,70 +52,147 @@ public class MeasuredText implements Spanned { // The sorted paragraph end offsets. private final @NonNull int[] mParagraphBreakPoints; - /** - * Build MeasuredText from the text. - * - * @param text The text to be measured. - * @param paint The paint to be used for drawing. - * @param textDir The text direction. - * @return The measured text. - */ - public static @NonNull MeasuredText build(@NonNull CharSequence text, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir) { - return MeasuredText.build(text, paint, textDir, 0, text.length()); - } + // The break strategy for this measured text. + private final @Layout.BreakStrategy int mBreakStrategy; + + // The hyphenation frequency for this measured text. + private final @Layout.HyphenationFrequency int mHyphenationFrequency; /** - * Build MeasuredText from the specific range of the text.. - * - * @param text The text to be measured. - * @param paint The paint to be used for drawing. - * @param textDir The text direction. - * @param start The inclusive start offset of the text. - * @param end The exclusive start offset of the text. - * @return The measured text. + * A Builder for MeasuredText */ - public static @NonNull MeasuredText build(@NonNull CharSequence text, - @NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir, - @IntRange(from = 0) int start, - @IntRange(from = 0) int end) { - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(paint); - Preconditions.checkNotNull(textDir); - Preconditions.checkArgumentInRange(start, 0, text.length(), "start"); - Preconditions.checkArgumentInRange(end, 0, text.length(), "end"); - - final IntArray paragraphEnds = new IntArray(); - final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); - - int paraEnd = 0; - for (int paraStart = start; paraStart < end; paraStart = paraEnd) { - paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); - if (paraEnd < 0) { - // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end. - paraEnd = end; - } else { - paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. - } + public static final class Builder { + // Mandatory parameters. + private final @NonNull CharSequence mText; + private final @NonNull TextPaint mPaint; + + // Members to be updated by setters. + private @IntRange(from = 0) int mStart; + private @IntRange(from = 0) int mEnd; + private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; + private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; + private @Layout.HyphenationFrequency int mHyphenationFrequency = + Layout.HYPHENATION_FREQUENCY_NORMAL; + + + /** + * Builder constructor + * + * @param text The text to be measured. + * @param paint The paint to be used for drawing. + */ + public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) { + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(paint); + + mText = text; + mPaint = paint; + mStart = 0; + mEnd = text.length(); + } - paragraphEnds.add(paraEnd); - measuredTexts.add(MeasuredParagraph.buildForStaticLayout( - paint, text, paraStart, paraEnd, textDir, null /* no recycle */)); + /** + * Set the range of measuring target. + * + * @param start The measuring target start offset in the text. + * @param end The measuring target end offset in the text. + */ + public @NonNull Builder setRange(@IntRange(from = 0) int start, + @IntRange(from = 0) int end) { + Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); + Preconditions.checkArgumentInRange(end, 0, mText.length(), "end"); + Preconditions.checkArgument(start <= end, "The range is reversed."); + + mStart = start; + mEnd = end; + return this; } - return new MeasuredText(text, start, end, paint, textDir, - measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), - paragraphEnds.toArray()); - } + /** + * Set the text direction heuristic + * + * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. + * + * @param textDir The text direction heuristic for resolving bidi behavior. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { + Preconditions.checkNotNull(textDir); + mTextDir = textDir; + return this; + } + + /** + * Set the break strategy + * + * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. + * + * @param breakStrategy The break strategy. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + + /** + * Set the hyphenation frequency + * + * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. + * + * @param hyphenationFrequency The hyphenation frequency. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setHyphenationFrequency( + @Layout.HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } - // Use MeasuredText.build instead. + /** + * Build the measured text + * + * @return the measured text. + */ + public @NonNull MeasuredText build() { + final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE + && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; + + final IntArray paragraphEnds = new IntArray(); + final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); + + int paraEnd = 0; + for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) { + paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd); + if (paraEnd < 0) { + // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph + // end. + paraEnd = mEnd; + } else { + paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. + } + + paragraphEnds.add(paraEnd); + measuredTexts.add(MeasuredParagraph.buildForStaticLayout( + mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, + null /* no recycle */)); + } + + return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, + mHyphenationFrequency, measuredTexts.toArray( + new MeasuredParagraph[measuredTexts.size()]), + paragraphEnds.toArray()); + } + }; + + // Use MeasuredText.Builder instead. private MeasuredText(@NonNull CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, + @Layout.BreakStrategy int breakStrategy, + @Layout.HyphenationFrequency int frequency, @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) { mText = text; @@ -125,6 +202,8 @@ public class MeasuredText implements Spanned { mMeasuredParagraphs = measuredTexts; mParagraphBreakPoints = paragraphBreakPoints; mTextDir = textDir; + mBreakStrategy = breakStrategy; + mHyphenationFrequency = frequency; } /** @@ -190,6 +269,20 @@ public class MeasuredText implements Spanned { return mMeasuredParagraphs[paraIndex]; } + /** + * Returns the break strategy for this text. + */ + public @Layout.BreakStrategy int getBreakStrategy() { + return mBreakStrategy; + } + + /** + * Returns the hyphenation frequency for this text. + */ + public @Layout.HyphenationFrequency int getHyphenationFrequency() { + return mHyphenationFrequency; + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Spanned overrides // diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 36bec863e7ac..70d648657d9d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -653,26 +653,40 @@ public class StaticLayout extends Layout { MeasuredText measured = null; final Spanned spanned; + final boolean canUseMeasuredText; if (source instanceof MeasuredText) { measured = (MeasuredText) source; - final CharSequence original = measured.getText(); - spanned = (original instanceof Spanned) ? (Spanned) original : null; - if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) { // The buffer position has changed. Re-measure here. - measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd); + canUseMeasuredText = false; + } else if (b.mBreakStrategy != measured.getBreakStrategy() + || b.mHyphenationFrequency != measured.getHyphenationFrequency()) { + // The computed hyphenation pieces may not be able to used. Re-measure it. + canUseMeasuredText = false; } else { // We can use measured information. - - // Overwrite with the one when emeasured. - // TODO: Give an option for developer not to overwrite and measure again here? - textDir = measured.getTextDir(); - paint = measured.getPaint(); + canUseMeasuredText = true; } } else { - measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd); + canUseMeasuredText = false; + } + + if (!canUseMeasuredText) { + measured = new MeasuredText.Builder(source, paint) + .setRange(bufStart, bufEnd) + .setTextDirection(textDir) + .setBreakStrategy(b.mBreakStrategy) + .setHyphenationFrequency(b.mHyphenationFrequency) + .build(); spanned = (source instanceof Spanned) ? (Spanned) source : null; + } else { + final CharSequence original = measured.getText(); + spanned = (original instanceof Spanned) ? (Spanned) original : null; + // Overwrite with the one when measured. + // TODO: Give an option for developer not to overwrite and measure again here? + textDir = measured.getTextDir(); + paint = measured.getPaint(); } try { diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index bbd9c9c0bac0..562ae7ada90a 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -358,7 +358,7 @@ public class Time { } /** - * Return the current time in YYYYMMDDTHHMMSS<tz> format + * Return the current time in YYYYMMDDTHHMMSS<tz> format */ @Override public String toString() { @@ -738,6 +738,7 @@ public class Time { * <p> * You should also use <tt>toMillis(false)</tt> if you want * to read back the same milliseconds that you set with {@link #set(long)} + * or {@link #set(Time)} or after parsing a date string. * * <p> * This method can return {@code -1} when the date / time fields have been @@ -745,8 +746,6 @@ public class Time { * For example, when daylight savings transitions cause an hour to be * skipped: times within that hour will return {@code -1} if isDst = * {@code -1}. - * - * or {@link #set(Time)} or after parsing a date string. */ public long toMillis(boolean ignoreDst) { calculator.copyFieldsFromTime(this); diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java index de05f50c9860..4f471a8a2f85 100644 --- a/core/java/android/text/style/BackgroundColorSpan.java +++ b/core/java/android/text/style/BackgroundColorSpan.java @@ -16,24 +16,48 @@ package android.text.style; +import android.annotation.ColorInt; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Changes the background color of the text to which the span is attached. + * <p> + * For example, to set a green background color for a text you would create a {@link + * android.text.SpannableStringBuilder} based on the text and set the span. + * <pre>{@code + * SpannableString string = new SpannableString("Text with a background color span"); + *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * }</pre> + * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" /> + * <figcaption>Set a background color for the text.</figcaption> + */ public class BackgroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private final int mColor; - public BackgroundColorSpan(int color) { + /** + * Creates a {@link BackgroundColorSpan} from a color integer. + * <p> + * + * @param color color integer that defines the background color + * @see android.content.res.Resources#getColor(int, Resources.Theme) + */ + public BackgroundColorSpan(@ColorInt int color) { mColor = color; } - public BackgroundColorSpan(Parcel src) { + /** + * Creates a {@link BackgroundColorSpan} from a parcel. + */ + public BackgroundColorSpan(@NonNull Parcel src) { mColor = src.readInt(); } - + public int getSpanTypeId() { return getSpanTypeIdInternal(); } @@ -42,26 +66,40 @@ public class BackgroundColorSpan extends CharacterStyle public int getSpanTypeIdInternal() { return TextUtils.BACKGROUND_COLOR_SPAN; } - + public int describeContents() { return 0; } - public void writeToParcel(Parcel dest, int flags) { + /** + * Flatten this object into a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); } + /** + * @return the background color of this span. + * @see BackgroundColorSpan#BackgroundColorSpan(int) + */ + @ColorInt public int getBackgroundColor() { return mColor; } + /** + * Updates the background color of the TextPaint. + */ @Override - public void updateDrawState(TextPaint ds) { - ds.bgColor = mColor; + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.bgColor = mColor; } } diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java index 2bc6d5406ade..08ab2a1f1a20 100644 --- a/core/java/android/text/style/ForegroundColorSpan.java +++ b/core/java/android/text/style/ForegroundColorSpan.java @@ -17,24 +17,48 @@ package android.text.style; import android.annotation.ColorInt; +import android.annotation.NonNull; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +/** + * Changes the color of the text to which the span is attached. + * <p> + * For example, to set a green text color you would create a {@link + * android.text.SpannableStringBuilder} based on the text and set the span. + * <pre>{@code + * SpannableString string = new SpannableString("Text with a foreground color span"); + *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + * }</pre> + * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" /> + * <figcaption>Set a text color.</figcaption> + */ public class ForegroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private final int mColor; + /** + * Creates a {@link ForegroundColorSpan} from a color integer. + * <p> + * To get the color integer associated with a particular color resource ID, use + * {@link android.content.res.Resources#getColor(int, Resources.Theme)} + * + * @param color color integer that defines the text color + */ public ForegroundColorSpan(@ColorInt int color) { mColor = color; } - public ForegroundColorSpan(Parcel src) { + /** + * Creates a {@link ForegroundColorSpan} from a parcel. + */ + public ForegroundColorSpan(@NonNull Parcel src) { mColor = src.readInt(); } - + public int getSpanTypeId() { return getSpanTypeIdInternal(); } @@ -48,22 +72,35 @@ public class ForegroundColorSpan extends CharacterStyle return 0; } - public void writeToParcel(Parcel dest, int flags) { + /** + * Flatten this object into a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { writeToParcelInternal(dest, flags); } /** @hide */ - public void writeToParcelInternal(Parcel dest, int flags) { + public void writeToParcelInternal(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); } + /** + * @return the foreground color of this span. + * @see ForegroundColorSpan#ForegroundColorSpan(int) + */ @ColorInt public int getForegroundColor() { return mColor; } + /** + * Updates the color of the TextPaint to the foreground color. + */ @Override - public void updateDrawState(TextPaint ds) { - ds.setColor(mColor); + public void updateDrawState(@NonNull TextPaint textPaint) { + textPaint.setColor(mColor); } } diff --git a/core/java/android/util/DataUnit.java b/core/java/android/util/DataUnit.java index 3cc1bd899eeb..ea4266ecb790 100644 --- a/core/java/android/util/DataUnit.java +++ b/core/java/android/util/DataUnit.java @@ -20,12 +20,15 @@ import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; /** - * Constants for common byte-related units. Note that both SI and IEC units are - * supported, and you'll need to pick the correct one for your use-case. + * A {@code DataUnit} represents data sizes at a given unit of granularity and + * provides utility methods to convert across units. + * <p> + * Note that both SI units (powers of 10) and IEC units (powers of 2) are + * supported, and you'll need to pick the correct one for your use-case. For + * example, Wikipedia defines a "kilobyte" as an SI unit of 1000 bytes, and a + * "kibibyte" as an IEC unit of 1024 bytes. * <p> * This design is mirrored after {@link TimeUnit} and {@link ChronoUnit}. - * - * @hide */ public enum DataUnit { KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } }, diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 82421566f7d3..e94f91a12905 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -44,6 +44,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); DEFAULT_FLAGS.put("settings_security_settings_v2", "true"); DEFAULT_FLAGS.put("settings_zone_picker_v2", "false"); + DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false"); } /** diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index ce8998fcb863..5a09dab552e3 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -16,6 +16,7 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; @@ -42,6 +43,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -105,7 +107,8 @@ public class ApkSignatureSchemeV2Verifier { */ public static X509Certificate[][] verify(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { - return verify(apkFile, true); + VerifiedSigner vSigner = verify(apkFile, true); + return vSigner.certs; } /** @@ -119,10 +122,11 @@ public class ApkSignatureSchemeV2Verifier { */ public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile) throws SignatureNotFoundException, SecurityException, IOException { - return verify(apkFile, false); + VerifiedSigner vSigner = verify(apkFile, false); + return vSigner.certs; } - private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity) + private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk, verifyIntegrity); @@ -138,7 +142,7 @@ public class ApkSignatureSchemeV2Verifier { * verify. * @throws IOException if an I/O error occurs while reading the APK file. */ - private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity) + private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { SignatureInfo signatureInfo = findSignature(apk); return verify(apk, signatureInfo, verifyIntegrity); @@ -163,7 +167,7 @@ public class ApkSignatureSchemeV2Verifier { * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it * against the APK file. */ - private static X509Certificate[][] verify( + private static VerifiedSigner verify( RandomAccessFile apk, SignatureInfo signatureInfo, boolean doVerifyIntegrity) throws SecurityException, IOException { @@ -207,7 +211,14 @@ public class ApkSignatureSchemeV2Verifier { ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } - return signerCerts.toArray(new X509Certificate[signerCerts.size()][]); + byte[] verityRootHash = null; + if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { + verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + } + + return new VerifiedSigner( + signerCerts.toArray(new X509Certificate[signerCerts.size()][]), + verityRootHash); } private static X509Certificate[] verifySigner( @@ -383,6 +394,24 @@ public class ApkSignatureSchemeV2Verifier { return; } + static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + VerifiedSigner vSigner = verify(apk, false); + return vSigner.verityRootHash; + } + } + + static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + } + } + private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: @@ -400,4 +429,20 @@ public class ApkSignatureSchemeV2Verifier { return false; } } + + /** + * Verified APK Signature Scheme v2 signer. + * + * @hide for internal use only. + */ + public static class VerifiedSigner { + public final X509Certificate[][] certs; + public final byte[] verityRootHash; + + public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) { + this.certs = certs; + this.verityRootHash = verityRootHash; + } + + } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index c9e67fe1c9ef..1b04eb2f7d44 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -16,6 +16,7 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256; import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512; @@ -43,6 +44,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -211,6 +213,10 @@ public class ApkSignatureSchemeV3Verifier { ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo); } + if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { + result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); + } + return result; } @@ -499,6 +505,24 @@ public class ApkSignatureSchemeV3Verifier { return new VerifiedProofOfRotation(certs, flagsList); } + static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + VerifiedSigner vSigner = verify(apk, false); + return vSigner.verityRootHash; + } + } + + static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + SignatureInfo signatureInfo = findSignature(apk); + return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo); + } + } + private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) { switch (sigAlgorithm) { case SIGNATURE_RSA_PSS_WITH_SHA256: @@ -541,6 +565,8 @@ public class ApkSignatureSchemeV3Verifier { public final X509Certificate[] certs; public final VerifiedProofOfRotation por; + public byte[] verityRootHash; + public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) { this.certs = certs; this.por = por; diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 555c4740389a..a2a76169c83a 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -36,7 +36,9 @@ import libcore.io.IoUtils; import java.io.IOException; import java.io.InputStream; +import java.security.DigestException; import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.ArrayList; @@ -367,4 +369,51 @@ public class ApkSignatureVerifier { // v2 didn't work, try jarsigner return verifyV1Signature(apkPath, false); } + + /** + * @return the verity root hash in the Signing Block. + */ + public static byte[] getVerityRootHash(String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + // first try v3 + try { + return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath); + } catch (SignatureNotFoundException e) { + // try older version + } + return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath); + } + + /** + * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code + * ByteBufferFactory}. + * + * @return the verity root hash of the generated Merkle tree. + */ + public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + // first try v3 + try { + return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory); + } catch (SignatureNotFoundException e) { + // try older version + } + return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory); + } + + /** + * Result of a successful APK verification operation. + */ + public static class Result { + public final Certificate[][] certs; + public final Signature[] sigs; + public final int signatureSchemeVersion; + + public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) { + this.certs = certs; + this.sigs = sigs; + this.signatureSchemeVersion = signingVersion; + } + } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 9d53847f8110..4146f6fab011 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -306,6 +306,26 @@ final class ApkSigningBlockUtils { } /** + * Generates the fsverity header and hash tree to be used by kernel for the given apk. This + * method does not check whether the root hash exists in the Signing Block or not. + * + * <p>The output is stored in the {@link ByteBuffer} created by the given {@link + * ByteBufferFactory}. + * + * @return the root hash of the generated hash tree. + */ + public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory, + SignatureInfo signatureInfo) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { + ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk, + signatureInfo, bufferFactory); + return result.rootHash; + } + } + + /** * Returns the ZIP End of Central Directory (EoCD) and its offset in the file. * * @throws IOException if an I/O error occurs while reading the file. diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 7412ef411fb4..a0d5e4c2dd8e 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -164,11 +164,11 @@ abstract class ApkVerityBuilder { } private void fillUpLastOutputChunk() { - int extra = (int) (BUFFER_SIZE - mOutput.position() % BUFFER_SIZE); - if (extra == 0) { + int lastBlockSize = (int) (mOutput.position() % BUFFER_SIZE); + if (lastBlockSize == 0) { return; } - mOutput.put(ByteBuffer.allocate(extra)); + mOutput.put(ByteBuffer.allocate(BUFFER_SIZE - lastBlockSize)); } } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 33fbf73b4371..1caea5773160 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -373,6 +373,7 @@ public final class Choreographer { * @see #removeCallbacks * @hide */ + @TestApi public void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0); } @@ -391,6 +392,7 @@ public final class Choreographer { * @see #removeCallback * @hide */ + @TestApi public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) { if (action == null) { @@ -440,6 +442,7 @@ public final class Choreographer { * @see #postCallbackDelayed * @hide */ + @TestApi public void removeCallbacks(int callbackType, Runnable action, Object token) { if (callbackType < 0 || callbackType > CALLBACK_LAST) { throw new IllegalArgumentException("callbackType is invalid"); diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 1ef5f0950b16..a61c8c1dba68 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -23,6 +23,8 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; @@ -30,8 +32,12 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; +import android.util.PathParser; import android.util.proto.ProtoOutputStream; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.List; @@ -43,6 +49,9 @@ import java.util.List; */ public final class DisplayCutout { + private static final String TAG = "DisplayCutout"; + private static final String DP_MARKER = "@dp"; + private static final Rect ZERO_RECT = new Rect(); private static final Region EMPTY_REGION = new Region(); @@ -312,6 +321,40 @@ public final class DisplayCutout { } /** + * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout. + * + * @hide + */ + public static DisplayCutout fromResources(Resources res, int displayWidth) { + String spec = res.getString(R.string.config_mainBuiltInDisplayCutout); + if (TextUtils.isEmpty(spec)) { + return null; + } + spec = spec.trim(); + final boolean inDp = spec.endsWith(DP_MARKER); + if (inDp) { + spec = spec.substring(0, spec.length() - DP_MARKER.length()); + } + + Path p; + try { + p = PathParser.createPathFromPathData(spec); + } catch (Throwable e) { + Log.wtf(TAG, "Could not inflate cutout: ", e); + return null; + } + + final Matrix m = new Matrix(); + if (inDp) { + final float dpToPx = res.getDisplayMetrics().density; + m.postScale(dpToPx, dpToPx); + } + m.postTranslate(displayWidth / 2f, 0); + p.transform(m); + return fromBounds(p); + } + + /** * Helper class for passing {@link DisplayCutout} through binder. * * Needed, because {@code readFromParcel} cannot be used with immutable classes. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index ed167c812be1..17b6ddca6394 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -29,6 +29,7 @@ import android.view.IWindowId; import android.view.MotionEvent; import android.view.WindowManager; import android.view.Surface; +import android.view.SurfaceControl; /** * System private per-application interface to the window manager. @@ -150,25 +151,32 @@ interface IWindowSession { boolean performHapticFeedback(IWindow window, int effectId, boolean always); /** - * Allocate the drag's thumbnail surface. Also assigns a token that identifies - * the drag to the OS and passes that as the return value. A return value of - * null indicates failure. - */ - IBinder prepareDrag(IWindow window, int flags, - int thumbnailWidth, int thumbnailHeight, out Surface outSurface); - - /** * Initiate the drag operation itself - */ - boolean performDrag(IWindow window, IBinder dragToken, int touchSource, + * + * @param window Window which initiates drag operation. + * @param flags See {@code View#startDragAndDrop} + * @param surface Surface containing drag shadow image + * @param touchSource See {@code InputDevice#getSource()} + * @param touchX TODO (b/72072998): Fix the issue that the system server misuse the arguments as + * initial touch point while the framework passes drag shadow size. + * @param touchY TODO (b/72072998): Fix the issue that the system server misuse the arguments as + * initial touch point while the framework passes drag shadow size. + * @param thumbCenterX X coordinate for the position within the shadow image that should be + * underneath the touch point during the drag and drop operation. + * @param thumbCenterY Y coordinate for the position within the shadow image that should be + * underneath the touch point during the drag and drop operation. + * @param data Data transferred by drag and drop + * @return Token of drag operation which will be passed to cancelDragAndDrop. + */ + IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); - /** + /** * Report the result of a drop action targeted to the given window. * consumed is 'true' when the drop was accepted by a valid recipient, * 'false' otherwise. */ - void reportDropResult(IWindow window, boolean consumed); + void reportDropResult(IWindow window, boolean consumed); /** * Cancel the current drag operation. @@ -236,4 +244,12 @@ interface IWindowSession { boolean startMovingTask(IWindow window, float startX, float startY); void updatePointerIcon(IWindow window); + + /** + * Update a tap exclude region with a rectangular area identified by provided id in the window. + * Touches on this region will not switch focus to this window. Passing an empty rect will + * remove the area from the exclude region of this window. + */ + void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width, + int height); } diff --git a/core/java/android/view/RemoteAnimationDefinition.aidl b/core/java/android/view/RemoteAnimationDefinition.aidl new file mode 100644 index 000000000000..32ecd01ebf25 --- /dev/null +++ b/core/java/android/view/RemoteAnimationDefinition.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.view; + +parcelable RemoteAnimationDefinition; diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java new file mode 100644 index 000000000000..381f6926a1e8 --- /dev/null +++ b/core/java/android/view/RemoteAnimationDefinition.java @@ -0,0 +1,93 @@ +/* + * 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.view; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.SparseArray; +import android.view.WindowManager.TransitionType; + +/** + * Defines which animation types should be overridden by which remote animation. + * + * @hide + */ +public class RemoteAnimationDefinition implements Parcelable { + + private final SparseArray<RemoteAnimationAdapter> mTransitionAnimationMap; + + public RemoteAnimationDefinition() { + mTransitionAnimationMap = new SparseArray<>(); + } + + /** + * Registers a remote animation for a specific transition. + * + * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values. + * @param adapter The adapter that described how to run the remote animation. + */ + public void addRemoteAnimation(@TransitionType int transition, RemoteAnimationAdapter adapter) { + mTransitionAnimationMap.put(transition, adapter); + } + + /** + * Checks whether a remote animation for specific transition is defined. + * + * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values. + * @return Whether this definition has defined a remote animation for the specified transition. + */ + public boolean hasTransition(@TransitionType int transition) { + return mTransitionAnimationMap.get(transition) != null; + } + + /** + * Retrieves the remote animation for a specific transition. + * + * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values. + * @return The remote animation adapter for the specified transition. + */ + public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition) { + return mTransitionAnimationMap.get(transition); + } + + public RemoteAnimationDefinition(Parcel in) { + mTransitionAnimationMap = in.readSparseArray(null /* loader */); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeSparseArray((SparseArray) mTransitionAnimationMap); + } + + public static final Creator<RemoteAnimationDefinition> CREATOR = + new Creator<RemoteAnimationDefinition>() { + public RemoteAnimationDefinition createFromParcel(Parcel in) { + return new RemoteAnimationDefinition(in); + } + + public RemoteAnimationDefinition[] newArray(int size) { + return new RemoteAnimationDefinition[size]; + } + }; +} diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 1f7f8b9a0c32..8830c90addac 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -283,6 +283,7 @@ public class Surface implements Parcelable { */ public long getNextFrameNumber() { synchronized (mLock) { + checkNotReleasedLocked(); return nativeGetNextFrameNumber(mNativeObject); } } diff --git a/core/java/android/os/Seccomp.java b/core/java/android/view/SurfaceControl.aidl index f14e93fe9403..744ead2be643 100644 --- a/core/java/android/os/Seccomp.java +++ b/core/java/android/view/SurfaceControl.aidl @@ -14,11 +14,6 @@ * limitations under the License. */ -package android.os; +package android.view; -/** - * @hide - */ -public final class Seccomp { - public static final native void setPolicy(); -} +parcelable SurfaceControl; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bd3be1b0445e..05770c357526 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4210,6 +4210,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static boolean sCanFocusZeroSized; + /** + * Always assign focus if a focusable View is available. + */ + private static boolean sAlwaysAssignFocus; + private String mTransitionName; static class TintInfo { @@ -4827,6 +4832,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sCanFocusZeroSized = targetSdkVersion < Build.VERSION_CODES.P; + sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P; + sCompatibilityDone = true; } } @@ -7017,8 +7024,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Called when this view wants to give up focus. If focus is cleared * {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called. * <p> - * <strong>Note:</strong> When a View clears focus the framework is trying - * to give focus to the first focusable View from the top. Hence, if this + * <strong>Note:</strong> When not in touch-mode, the framework will try to give focus + * to the first focusable View from the top after focus is cleared. Hence, if this * View is the first from the top that can take focus, then all callbacks * related to clearing focus will be invoked after which the framework will * give focus to this view. @@ -7029,7 +7036,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, System.out.println(this + " clearFocus()"); } - clearFocusInternal(null, true, true); + final boolean refocus = sAlwaysAssignFocus || !isInTouchMode(); + clearFocusInternal(null, true, refocus); } /** @@ -7224,20 +7232,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyEnterOrExitForAutoFillIfNeeded(gainFocus); } - private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) { - if (isAutofillable() && isAttachedToWindow()) { + /** @hide */ + public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) { + if (canNotifyAutofillEnterExitEvent()) { AutofillManager afm = getAutofillManager(); if (afm != null) { - if (enter && hasWindowFocus() && isFocused()) { + if (enter && isFocused()) { // We have not been laid out yet, hence cannot evaluate // whether this view is visible to the user, we will do // the evaluation once layout is complete. if (!isLaidOut()) { mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT; } else if (isVisibleToUser()) { + // TODO This is a potential problem that View gets focus before it's visible + // to User. Ideally View should handle the event when isVisibleToUser() + // becomes true where it should issue notifyViewEntered(). afm.notifyViewEntered(this); } - } else if (!hasWindowFocus() || !isFocused()) { + } else if (!isFocused()) { afm.notifyViewExited(this); } } @@ -8309,6 +8321,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, && getAutofillViewId() > LAST_APP_AUTOFILL_ID; } + /** @hide */ + public boolean canNotifyAutofillEnterExitEvent() { + return isAutofillable() && isAttachedToWindow(); + } + private void populateVirtualStructure(ViewStructure structure, AccessibilityNodeProvider provider, AccessibilityNodeInfo info) { structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), @@ -12443,8 +12460,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, imm.focusIn(this); } - notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus); - refreshDrawableState(); } @@ -17871,6 +17886,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Return the window this view is currently attached to. Used in + * {@link android.app.ActivityView} to communicate with WM. + * @hide + */ + protected IWindow getWindow() { + return mAttachInfo != null ? mAttachInfo.mWindow : null; + } + + /** * Return the visibility value of the least visible component passed. */ int combineVisibility(int vis1, int vis2) { @@ -23538,15 +23562,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0); } - boolean okay = false; - Point shadowSize = new Point(); Point shadowTouchPoint = new Point(); shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint); - if ((shadowSize.x < 0) || (shadowSize.y < 0) || - (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) { - throw new IllegalStateException("Drag shadow dimensions must not be negative"); + if ((shadowSize.x <= 0) || (shadowSize.y <= 0) + || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) { + throw new IllegalStateException("Drag shadow dimensions must be positive"); } if (ViewDebug.DEBUG_DRAG) { @@ -23557,40 +23579,50 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mAttachInfo.mDragSurface.release(); } mAttachInfo.mDragSurface = new Surface(); + mAttachInfo.mDragToken = null; + + final ViewRootImpl root = mAttachInfo.mViewRootImpl; + final SurfaceSession session = new SurfaceSession(root.mSurface); + final SurfaceControl surface = new SurfaceControl.Builder(session) + .setName("drag surface") + .setSize(shadowSize.x, shadowSize.y) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); try { - mAttachInfo.mDragToken = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow, - flags, shadowSize.x, shadowSize.y, mAttachInfo.mDragSurface); - if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" - + mAttachInfo.mDragToken + " surface=" + mAttachInfo.mDragSurface); - if (mAttachInfo.mDragToken != null) { - Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null); - try { - canvas.drawColor(0, PorterDuff.Mode.CLEAR); - shadowBuilder.onDrawShadow(canvas); - } finally { - mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas); - } - - final ViewRootImpl root = getViewRootImpl(); + mAttachInfo.mDragSurface.copyFrom(surface); + final Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null); + try { + canvas.drawColor(0, PorterDuff.Mode.CLEAR); + shadowBuilder.onDrawShadow(canvas); + } finally { + mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas); + } - // Cache the local state object for delivery with DragEvents - root.setLocalDragState(myLocalState); + // Cache the local state object for delivery with DragEvents + root.setLocalDragState(myLocalState); - // repurpose 'shadowSize' for the last touch point - root.getLastTouchPoint(shadowSize); + // repurpose 'shadowSize' for the last touch point + root.getLastTouchPoint(shadowSize); - okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, mAttachInfo.mDragToken, - root.getLastTouchSource(), shadowSize.x, shadowSize.y, - shadowTouchPoint.x, shadowTouchPoint.y, data); - if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay); + mAttachInfo.mDragToken = mAttachInfo.mSession.performDrag( + mAttachInfo.mWindow, flags, surface, root.getLastTouchSource(), + shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data); + if (ViewDebug.DEBUG_DRAG) { + Log.d(VIEW_LOG_TAG, "performDrag returned " + mAttachInfo.mDragToken); } + + return mAttachInfo.mDragToken != null; } catch (Exception e) { Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e); - mAttachInfo.mDragSurface.destroy(); - mAttachInfo.mDragSurface = null; + return false; + } finally { + if (mAttachInfo.mDragToken == null) { + mAttachInfo.mDragSurface.destroy(); + mAttachInfo.mDragSurface = null; + root.setLocalDragState(null); + } + session.kill(); } - - return okay; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fe3b6964047b..30f584c570ca 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -530,7 +530,7 @@ public final class ViewRootImpl implements ViewParent, mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); if (!sCompatibilityDone) { - sAlwaysAssignFocus = true; + sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P; sCompatibilityDone = true; } @@ -2337,7 +2337,7 @@ public final class ViewRootImpl implements ViewParent, } if (mFirst) { - if (sAlwaysAssignFocus) { + if (sAlwaysAssignFocus || !isInTouchMode()) { // handle first focus request if (DEBUG_INPUT_RESIZE) { Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus()); @@ -3608,7 +3608,7 @@ public final class ViewRootImpl implements ViewParent, checkThread(); if (mView != null) { if (!mView.hasFocus()) { - if (sAlwaysAssignFocus) { + if (sAlwaysAssignFocus || !isInTouchMode()) { v.requestFocus(); } } else { @@ -4211,10 +4211,7 @@ public final class ViewRootImpl implements ViewParent, // find the best view to give focus to in this brave new non-touch-mode // world - final View focused = focusSearch(null, View.FOCUS_DOWN); - if (focused != null) { - return focused.requestFocus(View.FOCUS_DOWN); - } + return mView.restoreDefaultFocus(); } return false; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 50d71185314f..3bb3a4c17b8f 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -105,6 +105,191 @@ public interface WindowManager extends ViewManager { final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer"; /** + * Not set up for a transition. + * @hide + */ + int TRANSIT_UNSET = -1; + + /** + * No animation for transition. + * @hide + */ + int TRANSIT_NONE = 0; + + /** + * A window in a new activity is being opened on top of an existing one in the same task. + * @hide + */ + int TRANSIT_ACTIVITY_OPEN = 6; + + /** + * The window in the top-most activity is being closed to reveal the previous activity in the + * same task. + * @hide + */ + int TRANSIT_ACTIVITY_CLOSE = 7; + + /** + * A window in a new task is being opened on top of an existing one in another activity's task. + * @hide + */ + int TRANSIT_TASK_OPEN = 8; + + /** + * A window in the top-most activity is being closed to reveal the previous activity in a + * different task. + * @hide + */ + int TRANSIT_TASK_CLOSE = 9; + + /** + * A window in an existing task is being displayed on top of an existing one in another + * activity's task. + * @hide + */ + int TRANSIT_TASK_TO_FRONT = 10; + + /** + * A window in an existing task is being put below all other tasks. + * @hide + */ + int TRANSIT_TASK_TO_BACK = 11; + + /** + * A window in a new activity that doesn't have a wallpaper is being opened on top of one that + * does, effectively closing the wallpaper. + * @hide + */ + int TRANSIT_WALLPAPER_CLOSE = 12; + + /** + * A window in a new activity that does have a wallpaper is being opened on one that didn't, + * effectively opening the wallpaper. + * @hide + */ + int TRANSIT_WALLPAPER_OPEN = 13; + + /** + * A window in a new activity is being opened on top of an existing one, and both are on top + * of the wallpaper. + * @hide + */ + int TRANSIT_WALLPAPER_INTRA_OPEN = 14; + + /** + * The window in the top-most activity is being closed to reveal the previous activity, and + * both are on top of the wallpaper. + * @hide + */ + int TRANSIT_WALLPAPER_INTRA_CLOSE = 15; + + /** + * A window in a new task is being opened behind an existing one in another activity's task. + * The new window will show briefly and then be gone. + * @hide + */ + int TRANSIT_TASK_OPEN_BEHIND = 16; + + /** + * A window in a task is being animated in-place. + * @hide + */ + int TRANSIT_TASK_IN_PLACE = 17; + + /** + * An activity is being relaunched (e.g. due to configuration change). + * @hide + */ + int TRANSIT_ACTIVITY_RELAUNCH = 18; + + /** + * A task is being docked from recents. + * @hide + */ + int TRANSIT_DOCK_TASK_FROM_RECENTS = 19; + + /** + * Keyguard is going away. + * @hide + */ + int TRANSIT_KEYGUARD_GOING_AWAY = 20; + + /** + * Keyguard is going away with showing an activity behind that requests wallpaper. + * @hide + */ + int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21; + + /** + * Keyguard is being occluded. + * @hide + */ + int TRANSIT_KEYGUARD_OCCLUDE = 22; + + /** + * Keyguard is being unoccluded. + * @hide + */ + int TRANSIT_KEYGUARD_UNOCCLUDE = 23; + + /** + * @hide + */ + @IntDef(prefix = { "TRANSIT_" }, value = { + TRANSIT_UNSET, + TRANSIT_NONE, + TRANSIT_ACTIVITY_OPEN, + TRANSIT_ACTIVITY_CLOSE, + TRANSIT_TASK_OPEN, + TRANSIT_TASK_CLOSE, + TRANSIT_TASK_TO_FRONT, + TRANSIT_TASK_TO_BACK, + TRANSIT_WALLPAPER_CLOSE, + TRANSIT_WALLPAPER_OPEN, + TRANSIT_WALLPAPER_INTRA_OPEN, + TRANSIT_WALLPAPER_INTRA_CLOSE, + TRANSIT_TASK_OPEN_BEHIND, + TRANSIT_TASK_IN_PLACE, + TRANSIT_ACTIVITY_RELAUNCH, + TRANSIT_DOCK_TASK_FROM_RECENTS, + TRANSIT_KEYGUARD_GOING_AWAY, + TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, + TRANSIT_KEYGUARD_OCCLUDE, + TRANSIT_KEYGUARD_UNOCCLUDE + }) + @Retention(RetentionPolicy.SOURCE) + @interface TransitionType {} + + /** + * Transition flag: Keyguard is going away, but keeping the notification shade open + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1; + + /** + * Transition flag: Keyguard is going away, but doesn't want an animation for it + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2; + + /** + * Transition flag: Keyguard is going away while it was showing the system wallpaper. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4; + + /** + * @hide + */ + @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { + TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION, + TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER, + }) + @Retention(RetentionPolicy.SOURCE) + @interface TransitionFlags {} + + /** * Exception that is thrown when trying to add view whose * {@link LayoutParams} {@link LayoutParams#token} * is invalid. diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 311dd4b8b294..3ee282ec3ac2 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -639,6 +639,8 @@ public class AccessibilityNodeInfo implements Parcelable { private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000; + private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000; + /** * Bits that provide the id of a virtual descendant of a view. */ @@ -2409,6 +2411,30 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Returns whether node represents a heading. + * + * @return {@code true} if the node is a heading, {@code false} otherwise. + */ + public boolean isHeading() { + return getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING); + } + + /** + * Sets whether the node represents a heading. + * + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param isHeading {@code true} if the node is a heading, {@code false} otherwise. + */ + public void setHeading(boolean isHeading) { + setBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING, isHeading); + } + + /** * Gets the package this node comes from. * * @return The package name. @@ -4597,7 +4623,8 @@ public class AccessibilityNodeInfo implements Parcelable { * @param rowSpan The number of rows the item spans. * @param columnIndex The column index at which the item is located. * @param columnSpan The number of columns the item spans. - * @param heading Whether the item is a heading. + * @param heading Whether the item is a heading. (Prefer + * {@link AccessibilityNodeInfo#setHeading(boolean)}). */ public static CollectionItemInfo obtain(int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) { @@ -4611,7 +4638,8 @@ public class AccessibilityNodeInfo implements Parcelable { * @param rowSpan The number of rows the item spans. * @param columnIndex The column index at which the item is located. * @param columnSpan The number of columns the item spans. - * @param heading Whether the item is a heading. + * @param heading Whether the item is a heading. (Prefer + * {@link AccessibilityNodeInfo#setHeading(boolean)}) * @param selected Whether the item is selected. */ public static CollectionItemInfo obtain(int rowIndex, int rowSpan, @@ -4698,6 +4726,7 @@ public class AccessibilityNodeInfo implements Parcelable { * heading, table header, etc. * * @return If the item is a heading. + * @deprecated Use {@link AccessibilityNodeInfo#isHeading()} */ public boolean isHeading() { return mHeading; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 78b41c6f4c7b..4b24a71c8bfb 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -53,6 +53,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -169,7 +170,6 @@ public final class AutofillManager { public static final String EXTRA_CLIENT_STATE = "android.view.autofill.extra.CLIENT_STATE"; - /** @hide */ public static final String EXTRA_RESTORE_SESSION_TOKEN = "android.view.autofill.extra.RESTORE_SESSION_TOKEN"; @@ -259,6 +259,12 @@ public final class AutofillManager { public static final int STATE_DISABLED_BY_SERVICE = 4; /** + * Timeout in ms for calls to the field classification service. + * @hide + */ + public static final int FC_SERVICE_TIMEOUT = 5000; + + /** * Makes an authentication id from a request id and a dataset id. * * @param requestId The request id. @@ -341,6 +347,10 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private AutofillId mSaveTriggerId; + /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */ + @GuardedBy("mLock") + private boolean mOnInvisibleCalled; + /** If set, session is commited when the activity is finished; otherwise session is canceled. */ @GuardedBy("mLock") private boolean mSaveOnFinish; @@ -397,6 +407,11 @@ public final class AutofillManager { boolean isVisibleForAutofill(); /** + * Client might disable enter/exit event e.g. when activity is paused. + */ + boolean isDisablingEnterExitEventForAutofill(); + + /** * Finds views by traversing the hierarchies of the client. * * @param viewIds The autofill ids of the views to find @@ -499,6 +514,19 @@ public final class AutofillManager { } /** + * Called once the client becomes invisible. + * + * @see AutofillClient#isVisibleForAutofill() + * + * {@hide} + */ + public void onInvisibleForAutofill() { + synchronized (mLock) { + mOnInvisibleCalled = true; + } + } + + /** * Save state before activity lifecycle * * @param outState Place to store the state @@ -623,21 +651,45 @@ public final class AutofillManager { return false; } + private boolean isClientVisibleForAutofillLocked() { + final AutofillClient client = getClient(); + return client != null && client.isVisibleForAutofill(); + } + + private boolean isClientDisablingEnterExitEvent() { + final AutofillClient client = getClient(); + return client != null && client.isDisablingEnterExitEventForAutofill(); + } + private void notifyViewEntered(@NonNull View view, int flags) { if (!hasAutofillFeature()) { return; } - AutofillCallback callback = null; + AutofillCallback callback; synchronized (mLock) { - if (shouldIgnoreViewEnteredLocked(view, flags)) return; + callback = notifyViewEnteredLocked(view, flags); + } - ensureServiceClientAddedIfNeededLocked(); + if (callback != null) { + mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); + } + } - if (!mEnabled) { - if (mCallback != null) { - callback = mCallback; - } - } else { + /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ + private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) { + if (shouldIgnoreViewEnteredLocked(view, flags)) return null; + + AutofillCallback callback = null; + + ensureServiceClientAddedIfNeededLocked(); + + if (!mEnabled) { + if (mCallback != null) { + callback = mCallback; + } + } else { + // don't notify entered when Activity is already in background + if (!isClientDisablingEnterExitEvent()) { final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); @@ -650,10 +702,7 @@ public final class AutofillManager { } } } - - if (callback != null) { - mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE); - } + return callback; } /** @@ -666,9 +715,16 @@ public final class AutofillManager { return; } synchronized (mLock) { - ensureServiceClientAddedIfNeededLocked(); + notifyViewExitedLocked(view); + } + } - if (mEnabled && isActiveLocked()) { + void notifyViewExitedLocked(@NonNull View view) { + ensureServiceClientAddedIfNeededLocked(); + + if (mEnabled && isActiveLocked()) { + // dont notify exited when Activity is already in background + if (!isClientDisablingEnterExitEvent()) { final AutofillId id = getAutofillId(view); // Update focus on existing session. @@ -719,7 +775,7 @@ public final class AutofillManager { } } if (mTrackedViews != null) { - mTrackedViews.notifyViewVisibilityChanged(id, isVisible); + mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible); } } } @@ -752,17 +808,32 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } - AutofillCallback callback = null; + AutofillCallback callback; synchronized (mLock) { - if (shouldIgnoreViewEnteredLocked(view, flags)) return; + callback = notifyViewEnteredLocked(view, virtualId, bounds, flags); + } - ensureServiceClientAddedIfNeededLocked(); + if (callback != null) { + callback.onAutofillEvent(view, virtualId, + AutofillCallback.EVENT_INPUT_UNAVAILABLE); + } + } - if (!mEnabled) { - if (mCallback != null) { - callback = mCallback; - } - } else { + /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ + private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds, + int flags) { + AutofillCallback callback = null; + if (shouldIgnoreViewEnteredLocked(view, flags)) return callback; + + ensureServiceClientAddedIfNeededLocked(); + + if (!mEnabled) { + if (mCallback != null) { + callback = mCallback; + } + } else { + // don't notify entered when Activity is already in background + if (!isClientDisablingEnterExitEvent()) { final AutofillId id = getAutofillId(view, virtualId); if (!isActiveLocked()) { @@ -774,11 +845,7 @@ public final class AutofillManager { } } } - - if (callback != null) { - callback.onAutofillEvent(view, virtualId, - AutofillCallback.EVENT_INPUT_UNAVAILABLE); - } + return callback; } /** @@ -792,9 +859,16 @@ public final class AutofillManager { return; } synchronized (mLock) { - ensureServiceClientAddedIfNeededLocked(); + notifyViewExitedLocked(view, virtualId); + } + } - if (mEnabled && isActiveLocked()) { + private void notifyViewExitedLocked(@NonNull View view, int virtualId) { + ensureServiceClientAddedIfNeededLocked(); + + if (mEnabled && isActiveLocked()) { + // don't notify exited when Activity is already in background + if (!isClientDisablingEnterExitEvent()) { final AutofillId id = getAutofillId(view, virtualId); // Update focus on existing session. @@ -1107,17 +1181,15 @@ public final class AutofillManager { * <a href="AutofillService.html#FieldClassification">field classification</a>. * * <p><b>Note:</b> This method should only be called by an app providing an autofill service, - * and it's ignored if the caller currently doesn't have an enabled autofill service for - * the user. - * - * @return list of all algorithms currently available, or an empty list if the caller currently - * does not have an enabled autofill service for the user. + * and it returns an empty list if the caller currently doesn't have an enabled autofill service + * for the user. */ @NonNull public List<String> getAvailableFieldClassificationAlgorithms() { + final String[] algorithms; try { - final List<String> names = mService.getAvailableFieldClassificationAlgorithms(); - return names != null ? names : Collections.emptyList(); + algorithms = mService.getAvailableFieldClassificationAlgorithms(); + return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList(); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; @@ -1155,7 +1227,7 @@ public final class AutofillManager { } /** @hide */ - public void onAuthenticationResult(int authenticationId, Intent data) { + public void onAuthenticationResult(int authenticationId, Intent data, View focusView) { if (!hasAutofillFeature()) { return; } @@ -1167,9 +1239,24 @@ public final class AutofillManager { if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data); synchronized (mLock) { - if (!isActiveLocked() || data == null) { + if (!isActiveLocked()) { return; } + // If authenticate activity closes itself during onCreate(), there is no onStop/onStart + // of app activity. We enforce enter event to re-show fill ui in such case. + // CTS example: + // LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt + // LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt + if (!mOnInvisibleCalled && focusView != null + && focusView.canNotifyAutofillEnterExitEvent()) { + notifyViewExitedLocked(focusView); + notifyViewEnteredLocked(focusView, 0); + } + if (data == null) { + // data is set to null when result is not RESULT_OK + return; + } + final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT); final Bundle responseData = new Bundle(); responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); @@ -1402,6 +1489,9 @@ public final class AutofillManager { if (sessionId == mSessionId) { final AutofillClient client = getClient(); if (client != null) { + // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill() + // before onAuthenticationResult() + mOnInvisibleCalled = false; client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent); } } @@ -1767,6 +1857,7 @@ public final class AutofillManager { pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled); pw.print(pfx); pw.print("hasService: "); pw.println(mService != null); pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null); + pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled); pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData); pw.print(pfx); pw.print("tracked views: "); if (mTrackedViews == null) { @@ -1937,15 +2028,13 @@ public final class AutofillManager { * @param id the id of the view/virtual view whose visibility changed. * @param isVisible visible if the view is visible in the view hierarchy. */ - void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) { - AutofillClient client = getClient(); - + void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) { if (sDebug) { Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible=" + isVisible); } - if (client != null && client.isVisibleForAutofill()) { + if (isClientVisibleForAutofillLocked()) { if (isVisible) { if (isInSet(mInvisibleTrackedIds, id)) { mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index 5cba21e3cc07..e80fdd93542c 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -78,8 +78,10 @@ public class AutofillPopupWindow extends PopupWindow { public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) { mWindowPresenter = new WindowPresenter(presenter); + setTouchModal(false); setOutsideTouchable(true); - setInputMethodMode(INPUT_METHOD_NEEDED); + setInputMethodMode(INPUT_METHOD_NOT_NEEDED); + setFocusable(true); } @Override diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 1afa35e80a26..1a11fbba0011 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteCallback; import android.service.autofill.FillEventHistory; import android.service.autofill.UserData; import android.view.autofill.AutofillId; @@ -58,6 +59,6 @@ interface IAutoFillManager { void setUserData(in UserData userData); boolean isFieldClassificationEnabled(); ComponentName getAutofillServiceComponentName(); - List<String> getAvailableFieldClassificationAlgorithms(); + String[] getAvailableFieldClassificationAlgorithms(); String getDefaultFieldClassificationAlgorithm(); } diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java index 003f221d08b2..1eb300eafb66 100644 --- a/core/java/android/view/inputmethod/ExtractedText.java +++ b/core/java/android/view/inputmethod/ExtractedText.java @@ -29,6 +29,8 @@ import android.text.TextUtils; public class ExtractedText implements Parcelable { /** * The text that has been extracted. + * + * @see android.widget.TextView#getText() */ public CharSequence text; @@ -88,6 +90,8 @@ public class ExtractedText implements Parcelable { /** * The hint that has been extracted. + * + * @see android.widget.TextView#getHint() */ public CharSequence hint; diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index b60209519f35..e9715c5121eb 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -277,8 +277,8 @@ public interface TextClassifier { /** * Returns a {@link Collection} of the entity types in the specified preset. * - * @see #ENTITIES_ALL - * @see #ENTITIES_NONE + * @see #ENTITY_PRESET_ALL + * @see #ENTITY_PRESET_NONE */ default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) { return Collections.EMPTY_LIST; diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 336c20cdcdc0..728824c2f0dc 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -106,6 +106,10 @@ public class EditText extends TextView { @Override public Editable getText() { CharSequence text = super.getText(); + // This can only happen during construction. + if (text == null) { + return null; + } if (text instanceof Editable) { return (Editable) super.getText(); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 247c806e928e..7bb0db1cf910 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3673,6 +3673,8 @@ public class Editor { mIsShowingUp = true; super.show(); } + + mSuggestionListView.setVisibility(mNumberOfSuggestions == 0 ? View.GONE : View.VISIBLE); } @Override diff --git a/core/java/android/widget/MediaController2.java b/core/java/android/widget/MediaControlView2.java index 9035137d5362..6e85ece291b2 100644 --- a/core/java/android/widget/MediaController2.java +++ b/core/java/android/widget/MediaControlView2.java @@ -19,130 +19,145 @@ package android.widget; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Canvas; import android.media.session.MediaController; import android.media.update.ApiLoader; -import android.media.update.MediaController2Provider; +import android.media.update.MediaControlView2Provider; import android.media.update.ViewProvider; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; /** * TODO PUBLIC API * @hide */ -public class MediaController2 extends FrameLayout { - private final MediaController2Provider mProvider; +public class MediaControlView2 extends FrameLayout { + private final MediaControlView2Provider mProvider; - public MediaController2(@NonNull Context context) { + public MediaControlView2(@NonNull Context context) { this(context, null); } - public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs) { + public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } - public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs, + public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } - public MediaController2(@NonNull Context context, @Nullable AttributeSet attrs, + public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mProvider = ApiLoader.getProvider(context) - .createMediaController2(this, new SuperProvider()); + .createMediaControlView2(this, new SuperProvider()); } - public void setController(MediaController controller) { - mProvider.setController_impl(controller); + public MediaControlView2Provider getProvider() { + return mProvider; } - public void setAnchorView(View view) { - mProvider.setAnchorView_impl(view); + /** + * TODO: add docs + */ + public void setController(MediaController controller) { + mProvider.setController_impl(controller); } + /** + * TODO: add docs + */ public void show() { mProvider.show_impl(); } + /** + * TODO: add docs + */ public void show(int timeout) { mProvider.show_impl(timeout); } + /** + * TODO: add docs + */ public boolean isShowing() { return mProvider.isShowing_impl(); } + /** + * TODO: add docs + */ public void hide() { mProvider.hide_impl(); } - public void setPrevNextListeners(OnClickListener next, OnClickListener prev) { - mProvider.setPrevNextListeners_impl(next, prev); - } - + /** + * TODO: add docs + */ public void showCCButton() { mProvider.showCCButton_impl(); } + /** + * TODO: add docs + */ public boolean isPlaying() { return mProvider.isPlaying_impl(); } + /** + * TODO: add docs + */ public int getCurrentPosition() { return mProvider.getCurrentPosition_impl(); } + /** + * TODO: add docs + */ public int getBufferPercentage() { return mProvider.getBufferPercentage_impl(); } + /** + * TODO: add docs + */ public boolean canPause() { return mProvider.canPause_impl(); } + /** + * TODO: add docs + */ public boolean canSeekBackward() { return mProvider.canSeekBackward_impl(); } + /** + * TODO: add docs + */ public boolean canSeekForward() { return mProvider.canSeekForward_impl(); } + /** + * TODO: add docs + */ public void showSubtitle() { mProvider.showSubtitle_impl(); } + /** + * TODO: add docs + */ public void hideSubtitle() { mProvider.hideSubtitle_impl(); } @Override - protected void onAttachedToWindow() { - mProvider.onAttachedToWindow_impl(); - } - - @Override - protected void onDetachedFromWindow() { - mProvider.onDetachedFromWindow_impl(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - mProvider.onLayout_impl(changed, left, top, right, bottom); - } - - @Override - public void draw(Canvas canvas) { - mProvider.draw_impl(canvas); - } - - @Override public CharSequence getAccessibilityClassName() { return mProvider.getAccessibilityClassName_impl(); } @@ -179,58 +194,38 @@ public class MediaController2 extends FrameLayout { private class SuperProvider implements ViewProvider { @Override - public void onAttachedToWindow_impl() { - MediaController2.super.onAttachedToWindow(); - } - - @Override - public void onDetachedFromWindow_impl() { - MediaController2.super.onDetachedFromWindow(); - } - - @Override - public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { - MediaController2.super.onLayout(changed, left, top, right, bottom); - } - - @Override - public void draw_impl(Canvas canvas) { - MediaController2.super.draw(canvas); - } - - @Override public CharSequence getAccessibilityClassName_impl() { - return MediaController2.super.getAccessibilityClassName(); + return MediaControlView2.super.getAccessibilityClassName(); } @Override public boolean onTouchEvent_impl(MotionEvent ev) { - return MediaController2.super.onTouchEvent(ev); + return MediaControlView2.super.onTouchEvent(ev); } @Override public boolean onTrackballEvent_impl(MotionEvent ev) { - return MediaController2.super.onTrackballEvent(ev); + return MediaControlView2.super.onTrackballEvent(ev); } @Override public boolean onKeyDown_impl(int keyCode, KeyEvent event) { - return MediaController2.super.onKeyDown(keyCode, event); + return MediaControlView2.super.onKeyDown(keyCode, event); } @Override public void onFinishInflate_impl() { - MediaController2.super.onFinishInflate(); + MediaControlView2.super.onFinishInflate(); } @Override public boolean dispatchKeyEvent_impl(KeyEvent event) { - return MediaController2.super.dispatchKeyEvent(event); + return MediaControlView2.super.dispatchKeyEvent(event); } @Override public void setEnabled_impl(boolean enabled) { - MediaController2.super.setEnabled(enabled); + MediaControlView2.super.setEnabled(enabled); } } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8c4e422d457b..7d3fcf469551 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -27,8 +27,10 @@ import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.FloatRange; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.Size; import android.annotation.StringRes; import android.annotation.StyleRes; @@ -52,6 +54,7 @@ import android.graphics.BaseCanvas; import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.Paint; +import android.graphics.Paint.FontMetricsInt; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.Rect; @@ -306,6 +309,7 @@ import java.util.Locale; * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize * @attr ref android.R.styleable#TextView_autoSizeStepGranularity * @attr ref android.R.styleable#TextView_autoSizePresetSizes + * @attr ref android.R.styleable#TextView_accessibilityHeading */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -400,6 +404,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mCurTextColor; private int mCurHintTextColor; private boolean mFreezesText; + private boolean mIsAccessibilityHeading; private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); @@ -924,6 +929,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int inputType = EditorInfo.TYPE_NULL; a = theme.obtainStyledAttributes( attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes); + int firstBaselineToTopHeight = -1; + int lastBaselineToBottomHeight = -1; + int lineHeight = -1; readTextAppearance(context, a, attributes, true /* styleArray */); @@ -1249,6 +1257,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_justificationMode: mJustificationMode = a.getInt(attr, Layout.JUSTIFICATION_MODE_NONE); break; + + case com.android.internal.R.styleable.TextView_firstBaselineToTopHeight: + firstBaselineToTopHeight = a.getDimensionPixelSize(attr, -1); + break; + + case com.android.internal.R.styleable.TextView_lastBaselineToBottomHeight: + lastBaselineToBottomHeight = a.getDimensionPixelSize(attr, -1); + break; + + case com.android.internal.R.styleable.TextView_lineHeight: + lineHeight = a.getDimensionPixelSize(attr, -1); + break; + case com.android.internal.R.styleable.TextView_accessibilityHeading: + mIsAccessibilityHeading = a.getBoolean(attr, false); } } @@ -1563,6 +1585,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE; } + + if (firstBaselineToTopHeight >= 0) { + setFirstBaselineToTopHeight(firstBaselineToTopHeight); + } + if (lastBaselineToBottomHeight >= 0) { + setLastBaselineToBottomHeight(lastBaselineToBottomHeight); + } + if (lineHeight >= 0) { + setLineHeight(lineHeight); + } } /** @@ -3165,6 +3197,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * @inheritDoc + * + * @see #setFirstBaselineToTopHeight(int) + * @see #setLastBaselineToBottomHeight(int) + */ @Override public void setPadding(int left, int top, int right, int bottom) { if (left != mPaddingLeft @@ -3179,6 +3217,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener invalidate(); } + /** + * @inheritDoc + * + * @see #setFirstBaselineToTopHeight(int) + * @see #setLastBaselineToBottomHeight(int) + */ @Override public void setPaddingRelative(int start, int top, int end, int bottom) { if (start != getPaddingStart() @@ -3194,6 +3238,97 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Updates the top padding of the TextView so that {@code firstBaselineToTopHeight} is + * equal to the distance between the firt text baseline and the top of this TextView. + * <strong>Note</strong> that if {@code FontMetrics.top} or {@code FontMetrics.ascent} was + * already greater than {@code firstBaselineToTopHeight}, the top padding is not updated. + * + * @param firstBaselineToTopHeight distance between first baseline to top of the container + * in pixels + * + * @see #getFirstBaselineToTopHeight() + * @see #setPadding(int, int, int, int) + * @see #setPaddingRelative(int, int, int, int) + * + * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight + */ + public void setFirstBaselineToTopHeight(@Px @IntRange(from = 0) int firstBaselineToTopHeight) { + Preconditions.checkArgumentNonnegative(firstBaselineToTopHeight); + + final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt(); + final int fontMetricsTop; + if (getIncludeFontPadding()) { + fontMetricsTop = fontMetrics.top; + } else { + fontMetricsTop = fontMetrics.ascent; + } + + // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size + // in settings). At the moment, we don't. + + if (firstBaselineToTopHeight > Math.abs(fontMetricsTop)) { + final int paddingTop = firstBaselineToTopHeight - (-fontMetricsTop); + setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), getPaddingBottom()); + } + } + + /** + * Updates the bottom padding of the TextView so that {@code lastBaselineToBottomHeight} is + * equal to the distance between the last text baseline and the bottom of this TextView. + * <strong>Note</strong> that if {@code FontMetrics.bottom} or {@code FontMetrics.descent} was + * already greater than {@code lastBaselineToBottomHeight}, the bottom padding is not updated. + * + * @param lastBaselineToBottomHeight distance between last baseline to bottom of the container + * in pixels + * + * @see #getLastBaselineToBottomHeight() + * @see #setPadding(int, int, int, int) + * @see #setPaddingRelative(int, int, int, int) + * + * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight + */ + public void setLastBaselineToBottomHeight( + @Px @IntRange(from = 0) int lastBaselineToBottomHeight) { + Preconditions.checkArgumentNonnegative(lastBaselineToBottomHeight); + + final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt(); + final int fontMetricsBottom; + if (getIncludeFontPadding()) { + fontMetricsBottom = fontMetrics.bottom; + } else { + fontMetricsBottom = fontMetrics.descent; + } + + // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size + // in settings). At the moment, we don't. + + if (lastBaselineToBottomHeight > Math.abs(fontMetricsBottom)) { + final int paddingBottom = lastBaselineToBottomHeight - fontMetricsBottom; + setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom); + } + } + + /** + * Returns the distance between the first text baseline and the top of this TextView. + * + * @see #setFirstBaselineToTopHeight(int) + * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight + */ + public int getFirstBaselineToTopHeight() { + return getPaddingTop() - getPaint().getFontMetricsInt().top; + } + + /** + * Returns the distance between the last text baseline and the bottom of this TextView. + * + * @see #setLastBaselineToBottomHeight(int) + * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight + */ + public int getLastBaselineToBottomHeight() { + return getPaddingBottom() + getPaint().getFontMetricsInt().bottom; + } + + /** * Gets the autolink mask of the text. See {@link * android.text.util.Linkify#ALL Linkify.ALL} and peers for * possible values. @@ -3756,6 +3891,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @param elegant set the paint's elegant metrics flag. * + * @see #isElegantTextHeight() * @see Paint#isElegantTextHeight() * * @attr ref android.R.styleable#TextView_elegantTextHeight @@ -4974,6 +5110,53 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets an explicit line height for this TextView. This is equivalent to the vertical distance + * between subsequent baselines in the TextView. + * + * @param lineHeight the line height in pixels + * + * @see #setLineSpacing(float, float) + * @see #getLineSpacing() + * + * @attr ref android.R.styleable#TextView_lineHeight + */ + public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) { + Preconditions.checkArgumentNonnegative(lineHeight); + + final int fontHeight = getPaint().getFontMetricsInt(null); + // Make sure we don't setLineSpacing if it's not needed to avoid unnecessary redraw. + if (lineHeight != fontHeight) { + // Set lineSpacingExtra by the difference of lineSpacing with lineHeight + setLineSpacing(lineHeight - fontHeight, 1f); + } + } + + /** + * Gets whether this view is a heading for accessibility purposes. + * + * @return {@code true} if the view is a heading, {@code false} otherwise. + * + * @attr ref android.R.styleable#TextView_accessibilityHeading + */ + public boolean isAccessibilityHeading() { + return mIsAccessibilityHeading; + } + + /** + * Set if view is a heading for a section of content for accessibility purposes. + * + * @param isHeading {@code true} if the view is a heading, {@code false} otherwise. + * + * @attr ref android.R.styleable#TextView_accessibilityHeading + */ + public void setAccessibilityHeading(boolean isHeading) { + if (isHeading != mIsAccessibilityHeading) { + mIsAccessibilityHeading = isHeading; + notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + } + + /** * Convenience method to append the specified text to the TextView's * display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE} * if it was not already editable. @@ -9125,8 +9308,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * - * Checks whether the transformation method applied to this TextView is set to ALL CAPS. This - * settings is internally ignored if this field is editable or selectable. + * Checks whether the transformation method applied to this TextView is set to ALL CAPS. * @return Whether the current transformation method is for ALL CAPS. * * @see #setAllCaps(boolean) @@ -10524,6 +10706,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener info.setText(getTextForAccessibility()); info.setHintText(mHint); info.setShowingHintText(isShowingHint()); + info.setHeading(mIsAccessibilityHeading); if (mBufferType == BufferType.EDITABLE) { info.setEditable(true); diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java index 310a7bbdcabf..955f0532471a 100644 --- a/core/java/android/widget/VideoView2.java +++ b/core/java/android/widget/VideoView2.java @@ -20,9 +20,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Canvas; import android.media.AudioAttributes; -import android.media.MediaPlayer; +import android.media.AudioManager; import android.media.update.ApiLoader; import android.media.update.VideoView2Provider; import android.media.update.ViewProvider; @@ -80,7 +79,8 @@ public class VideoView2 extends FrameLayout { int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider()); + mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider(), + attrs, defStyleAttr, defStyleRes); } /** @@ -93,6 +93,20 @@ public class VideoView2 extends FrameLayout { /** * @hide */ + public void setMediaControlView2(MediaControlView2 mediaControlView) { + mProvider.setMediaControlView2_impl(mediaControlView); + } + + /** + * @hide + */ + public MediaControlView2 getMediaControlView2() { + return mProvider.getMediaControlView2_impl(); + } + + /** + * @hide + */ public void start() { mProvider.start_impl(); } @@ -161,6 +175,45 @@ public class VideoView2 extends FrameLayout { } /** + * Sets playback speed. + * + * It is expressed as a multiplicative factor, where normal speed is 1.0f. If it is less than + * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the + * maximum speed that internal engine supports, system will determine best handling or it will + * be reset to the normal speed 1.0f. + * TODO: This should be revised after integration with MediaPlayer2. + * @param speed the playback speed. It should be positive. + * @hide + */ + public void setSpeed(float speed) { + mProvider.setSpeed_impl(speed); + } + + /** + * Returns current speed setting. + * + * If setSpeed() has never been called, returns the default value 1.0f. + * @return current speed setting + * @hide + */ + public float getSpeed() { + return mProvider.getSpeed_impl(); + } + + /** + * Sets which type of audio focus will be requested during the playback, or configures playback + * to not request audio focus. Valid values for focus requests are + * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, + * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and + * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use + * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be + * requested when playback starts. You can for instance use this when playing a silent animation + * through this class, and you don't want to affect other audio applications playing in the + * background. + * + * @param focusGain the type of audio focus gain that will be requested, or + * {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during playback. + * * @hide */ public void setAudioFocusRequest(int focusGain) { @@ -168,6 +221,10 @@ public class VideoView2 extends FrameLayout { } /** + * Sets the {@link AudioAttributes} to be used during the playback of the video. + * + * @param attributes non-null <code>AudioAttributes</code>. + * * @hide */ public void setAudioAttributes(@NonNull AudioAttributes attributes) { @@ -175,6 +232,9 @@ public class VideoView2 extends FrameLayout { } /** + * Sets video path. + * + * @param path the path of the video. * @hide */ public void setVideoPath(String path) { @@ -198,13 +258,6 @@ public class VideoView2 extends FrameLayout { /** * @hide */ - public void setMediaController2(MediaController2 controllerView) { - mProvider.setMediaController2_impl(controllerView); - } - - /** - * @hide - */ public void setViewType(@ViewType int viewType) { mProvider.setViewType_impl(viewType); } @@ -227,28 +280,28 @@ public class VideoView2 extends FrameLayout { /** * @hide */ - public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) { + public void setOnPreparedListener(OnPreparedListener l) { mProvider.setOnPreparedListener_impl(l); } /** * @hide */ - public void setOnCompletionListener(MediaPlayer.OnCompletionListener l) { + public void setOnCompletionListener(OnCompletionListener l) { mProvider.setOnCompletionListener_impl(l); } /** * @hide */ - public void setOnErrorListener(MediaPlayer.OnErrorListener l) { + public void setOnErrorListener(OnErrorListener l) { mProvider.setOnErrorListener_impl(l); } /** * @hide */ - public void setOnInfoListener(MediaPlayer.OnInfoListener l) { + public void setOnInfoListener(OnInfoListener l) { mProvider.setOnInfoListener_impl(l); } @@ -260,15 +313,61 @@ public class VideoView2 extends FrameLayout { } /** + * Interface definition of a callback to be invoked when the viw type has been changed. * @hide */ public interface OnViewTypeChangedListener { /** - * @hide + * Called when the view type has been changed. + * @see VideoView2#setViewType(int) */ void onViewTypeChanged(@ViewType int viewType); } + /** + * @hide + */ + public interface OnPreparedListener { + /** + * Called when the media file is ready for playback. + */ + void onPrepared(); + } + + /** + * @hide + */ + public interface OnCompletionListener { + /** + * Called when the end of a media source is reached during playback. + */ + void onCompletion(); + } + + /** + * @hide + */ + public interface OnErrorListener { + /** + * Called to indicate an error. + */ + boolean onError(int what, int extra); + } + + /** + * @hide + */ + public interface OnInfoListener { + /** + * Called to indicate an info or a warning. + * @see MediaPlayer#OnInfoListener + * + * @param what the type of info or warning. + * @param extra an extra code, specific to the info. + */ + void onInfo(int what, int extra); + } + @Override public CharSequence getAccessibilityClassName() { return mProvider.getAccessibilityClassName_impl(); @@ -306,26 +405,6 @@ public class VideoView2 extends FrameLayout { private class SuperProvider implements ViewProvider { @Override - public void onAttachedToWindow_impl() { - VideoView2.super.onAttachedToWindow(); - } - - @Override - public void onDetachedFromWindow_impl() { - VideoView2.super.onDetachedFromWindow(); - } - - @Override - public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { - VideoView2.super.onLayout(changed, left, top, right, bottom); - } - - @Override - public void draw_impl(Canvas canvas) { - VideoView2.super.draw(canvas); - } - - @Override public CharSequence getAccessibilityClassName_impl() { return VideoView2.super.getAccessibilityClassName(); } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 388180dc61e0..e2d1ad59043e 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -23,6 +23,7 @@ import android.net.wifi.WifiActivityEnergyInfo; import android.os.ParcelFileDescriptor; import android.os.WorkSource; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; @@ -91,6 +92,7 @@ interface IBatteryStats { void noteVibratorOff(int uid); void noteStartGps(int uid); void noteStopGps(int uid); + void noteGpsSignalQuality(int signalLevel); void noteScreenState(int state); void noteScreenBrightness(int brightness); void noteUserActivity(int uid, int event); @@ -140,6 +142,9 @@ interface IBatteryStats { /** {@hide} */ CellularBatteryStats getCellularBatteryStats(); + /** {@hide} */ + GpsBatteryStats getGpsBatteryStats(); + HealthStatsParceler takeUidSnapshot(int uid); HealthStatsParceler[] takeUidSnapshots(in int[] uid); diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index e765ab1eae2f..8a456d1c6f58 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -25,6 +25,7 @@ import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser.PackageLite; +import android.content.pm.dex.DexMetadataHelper; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -415,6 +416,9 @@ public class PackageHelper { sizeBytes += codeFile.length(); } + // Include raw dex metadata files + sizeBytes += DexMetadataHelper.getPackageDexMetadataSize(pkg); + // Include all relevant native code sizeBytes += NativeLibraryHelper.sumNativeBinariesWithOverride(handle, abiOverride); diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java index 5eda81baa364..02cd09f78af3 100644 --- a/core/java/com/android/internal/net/NetworkStatsFactory.java +++ b/core/java/com/android/internal/net/NetworkStatsFactory.java @@ -219,7 +219,7 @@ public class NetworkStatsFactory { } NetworkStats.Entry adjust = - new NetworkStats.Entry(baseIface, 0, 0, 0, 0L, 0L, 0L, 0L, 0L); + new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L); // Subtract any 464lat traffic seen for the root UID on the current base interface. adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA); adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA); diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 15dc6f507093..5a59e70865e5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -665,14 +665,14 @@ public class BatteryStatsHelper { /** * Calculate the baseline power usage for the device when it is in suspend and idle. - * The device is drawing POWER_CPU_IDLE power at its lowest power state. - * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held. + * The device is drawing POWER_CPU_SUSPEND power at its lowest power state. + * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held. */ private void addIdleUsage() { final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); + mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND); final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) * - mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); + mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); if (DEBUG && totalPowerMah != 0) { Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index ac5dbc4e175a..799e3e8d38d0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -34,6 +34,7 @@ import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.FileUtils; import android.os.Handler; import android.os.IBatteryPropertiesRegistrar; @@ -78,6 +79,7 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; @@ -153,11 +155,11 @@ public class BatteryStatsImpl extends BatteryStats { MAX_HISTORY_BUFFER = 96*1024; // 96KB MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB } else { - MAX_HISTORY_ITEMS = 2000; - MAX_MAX_HISTORY_ITEMS = 3000; - MAX_WAKELOCKS_PER_UID = 100; - MAX_HISTORY_BUFFER = 256*1024; // 256KB - MAX_MAX_HISTORY_BUFFER = 320*1024; // 256KB + MAX_HISTORY_ITEMS = 4000; + MAX_MAX_HISTORY_ITEMS = 6000; + MAX_WAKELOCKS_PER_UID = 200; + MAX_HISTORY_BUFFER = 512*1024; // 512KB + MAX_MAX_HISTORY_BUFFER = 640*1024; // 640KB } } @@ -198,6 +200,12 @@ public class BatteryStatsImpl extends BatteryStats { protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader(); @VisibleForTesting + protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader = + new KernelUidCpuActiveTimeReader(); + @VisibleForTesting + protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader = + new KernelUidCpuClusterTimeReader(); + @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats @@ -666,6 +674,10 @@ public class BatteryStatsImpl extends BatteryStats { int mCameraOnNesting; StopwatchTimer mCameraOnTimer; + int mGpsSignalQualityBin = -1; + final StopwatchTimer[] mGpsSignalQualityTimer = + new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + int mPhoneSignalStrengthBin = -1; int mPhoneSignalStrengthBinRaw = -1; final StopwatchTimer[] mPhoneSignalStrengthsTimer = @@ -3880,6 +3892,8 @@ public class BatteryStatsImpl extends BatteryStats { } mKernelUidCpuTimeReader.removeUid(isolatedUid); mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); + mKernelUidCpuActiveTimeReader.removeUid(isolatedUid); + mKernelUidCpuClusterTimeReader.removeUid(isolatedUid); } public int mapUid(int uid) { @@ -4575,10 +4589,37 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: " + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtime, uptime); + stopAllGpsSignalQualityTimersLocked(-1); + mGpsSignalQualityBin = -1; } getUidStatsLocked(uid).noteStopGps(elapsedRealtime); } + public void noteGpsSignalQualityLocked(int signalLevel) { + if (mGpsNesting == 0) { + return; + } + if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) { + stopAllGpsSignalQualityTimersLocked(-1); + return; + } + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + if (mGpsSignalQualityBin != signalLevel) { + if (mGpsSignalQualityBin >= 0) { + mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime); + } + if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) { + mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime); + } + mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK) + | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT); + addHistoryRecordLocked(elapsedRealtime, uptime); + mGpsSignalQualityBin = signalLevel; + } + return; + } + public void noteScreenStateLocked(int state) { state = mPretendScreenOff ? Display.STATE_OFF : state; @@ -4912,6 +4953,18 @@ public class BatteryStatsImpl extends BatteryStats { mDailyPackageChanges.add(pc); } + void stopAllGpsSignalQualityTimersLocked(int except) { + final long elapsedRealtime = mClocks.elapsedRealtime(); + for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + if (i == except) { + continue; + } + while (mGpsSignalQualityTimer[i].isRunningLocked()) { + mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime); + } + } + } + public void notePhoneOnLocked() { if (!mPhoneOn) { final long elapsedRealtime = mClocks.elapsedRealtime(); @@ -6123,6 +6176,20 @@ public class BatteryStatsImpl extends BatteryStats { return val; } + @Override public long getGpsSignalQualityTime(int strengthBin, + long elapsedRealtimeUs, int which) { + if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) { + return 0; + } + return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked( + elapsedRealtimeUs, which); + } + + @Override public long getGpsBatteryDrainMaMs() { + //TODO: Add GPS power computation (b/67213967) + return 0; + } + @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) { return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -6479,9 +6546,11 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime; LongSamplingCounter mSystemCpuTime; LongSamplingCounter[][] mCpuClusterSpeedTimesUs; + LongSamplingCounter mCpuActiveTimeMs; LongSamplingCounterArray mCpuFreqTimeMs; LongSamplingCounterArray mScreenOffCpuFreqTimeMs; + LongSamplingCounterArray mCpuClusterTimesMs; LongSamplingCounterArray[] mProcStateTimeMs; LongSamplingCounterArray[] mProcStateScreenOffTimeMs; @@ -6551,6 +6620,8 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase); + mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase); mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) { @Override public Wakelock instantiateObject() { @@ -6598,6 +6669,17 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long getCpuActiveTime() { + return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED); + } + + @Override + public long[] getCpuClusterTimes() { + return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED); + } + + + @Override public long[] getCpuFreqTimes(int which, int procState) { if (which < 0 || which >= NUM_PROCESS_STATE) { return null; @@ -7660,6 +7742,9 @@ public class BatteryStatsImpl extends BatteryStats { mScreenOffCpuFreqTimeMs.reset(false); } + mCpuActiveTimeMs.reset(false); + mCpuClusterTimesMs.reset(false); + if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { if (counters != null) { @@ -7864,6 +7949,8 @@ public class BatteryStatsImpl extends BatteryStats { if (mScreenOffCpuFreqTimeMs != null) { mScreenOffCpuFreqTimeMs.detach(); } + mCpuActiveTimeMs.detach(); + mCpuClusterTimesMs.detach(); if (mProcStateTimeMs != null) { for (LongSamplingCounterArray counters : mProcStateTimeMs) { @@ -8139,6 +8226,10 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs); LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs); + + mCpuActiveTimeMs.writeToParcel(out); + mCpuClusterTimesMs.writeToParcel(out); + if (mProcStateTimeMs != null) { out.writeInt(mProcStateTimeMs.length); for (LongSamplingCounterArray counters : mProcStateTimeMs) { @@ -8456,6 +8547,9 @@ public class BatteryStatsImpl extends BatteryStats { mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel( in, mBsi.mOnBatteryScreenOffTimeBase); + mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); + mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in); + int length = in.readInt(); if (length == NUM_PROCESS_STATE) { mProcStateTimeMs = new LongSamplingCounterArray[length]; @@ -9822,6 +9916,10 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null, + mOnBatteryTimeBase); + } mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase); mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase); mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase); @@ -10511,6 +10609,9 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i].reset(false); } mWifiMulticastWakelockTimer.reset(false); + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].reset(false); + } mWifiActivity.reset(false); mBluetoothActivity.reset(false); mModemActivity.reset(false); @@ -11437,6 +11538,8 @@ public class BatteryStatsImpl extends BatteryStats { if (!mOnBatteryInternal) { mKernelUidCpuTimeReader.readDelta(null); mKernelUidCpuFreqTimeReader.readDelta(null); + mKernelUidCpuActiveTimeReader.readDelta(null); + mKernelUidCpuClusterTimeReader.readDelta(null); for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } @@ -11453,6 +11556,8 @@ public class BatteryStatsImpl extends BatteryStats { updateClusterSpeedTimes(updatedUids); } readKernelUidCpuFreqTimesLocked(partialTimersToConsider); + readKernelUidCpuActiveTimesLocked(); + readKernelUidCpuClusterTimesLocked(); } /** @@ -11764,6 +11869,64 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Take a snapshot of the cpu active times spent by each uid and update the corresponding + * counters. + */ + @VisibleForTesting + public void readKernelUidCpuActiveTimesLocked() { + final long startTimeMs = mClocks.uptimeMillis(); + mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + mKernelUidCpuActiveTimeReader.removeUid(uid); + Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid); + return; + } + if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { + Slog.w(TAG, "Got active times for an invalid user's uid " + uid); + mKernelUidCpuActiveTimeReader.removeUid(uid); + return; + } + final Uid u = getUidStatsLocked(uid); + u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs); + }); + + final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; + if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) { + Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms"); + } + } + + /** + * Take a snapshot of the cpu cluster times spent by each uid and update the corresponding + * counters. + */ + @VisibleForTesting + public void readKernelUidCpuClusterTimesLocked() { + final long startTimeMs = mClocks.uptimeMillis(); + mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { + uid = mapUid(uid); + if (Process.isIsolated(uid)) { + mKernelUidCpuClusterTimeReader.removeUid(uid); + Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid); + return; + } + if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { + Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid); + mKernelUidCpuClusterTimeReader.removeUid(uid); + return; + } + final Uid u = getUidStatsLocked(uid); + u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs); + }); + + final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; + if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) { + Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms"); + } + } + boolean setChargingLocked(boolean charging) { if (mCharging != charging) { mCharging = charging; @@ -12367,6 +12530,21 @@ public class BatteryStatsImpl extends BatteryStats { return s; } + /*@hide */ + public GpsBatteryStats getGpsBatteryStats() { + GpsBatteryStats s = new GpsBatteryStats(); + final int which = STATS_SINCE_CHARGED; + final long rawRealTime = SystemClock.elapsedRealtime() * 1000; + s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000); + s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs()); + long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS]; + for (int i=0; i<time.length; i++) { + time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000; + } + s.setTimeInGpsSignalQualityLevel(time); + return s; + } + @Override public LevelStepTracker getChargeLevelStepTracker() { return mChargeStepTracker; @@ -13044,6 +13222,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in); } + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in); + } mWifiActivity.readSummaryFromParcel(in); mBluetoothActivity.readSummaryFromParcel(in); mModemActivity.readSummaryFromParcel(in); @@ -13249,6 +13430,10 @@ public class BatteryStatsImpl extends BatteryStats { in, mOnBatteryTimeBase); u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked( in, mOnBatteryScreenOffTimeBase); + + u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in); + u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in); + int length = in.readInt(); if (length == Uid.NUM_PROCESS_STATE) { u.mProcStateTimeMs = new LongSamplingCounterArray[length]; @@ -13482,6 +13667,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); + } mWifiActivity.writeSummaryToParcel(out); mBluetoothActivity.writeSummaryToParcel(out); mModemActivity.writeSummaryToParcel(out); @@ -13725,6 +13913,9 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs); LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs); + u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out); + u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out); + if (u.mProcStateTimeMs != null) { out.writeInt(u.mProcStateTimeMs.length); for (LongSamplingCounterArray counters : u.mProcStateTimeMs) { @@ -13954,7 +14145,10 @@ public class BatteryStatsImpl extends BatteryStats { mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null, mOnBatteryTimeBase, in); } - + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, + null, mOnBatteryTimeBase, in); + } mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS, in); mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, @@ -14154,6 +14348,9 @@ public class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) { mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime); } + for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime); + } mWifiActivity.writeToParcel(out, 0); mBluetoothActivity.writeToParcel(out, 0); mModemActivity.writeToParcel(out, 0); @@ -14348,6 +14545,10 @@ public class BatteryStatsImpl extends BatteryStats { pr.println("*** Wifi signal strength #" + i + ":"); mWifiSignalStrengthsTimer[i].logState(pr, " "); } + for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) { + pr.println("*** GPS signal quality #" + i + ":"); + mGpsSignalQualityTimer[i].logState(pr, " "); + } pr.println("*** Flashlight timer:"); mFlashlightOnTimer.logState(pr, " "); pr.println("*** Camera timer:"); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index bb743c157d1f..a34e7f50c9c9 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -31,8 +31,7 @@ public class CpuPowerCalculator extends PowerCalculator { @Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - + long rawUptimeUs, int statsType) { app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; final int numClusters = mProfile.getNumCpuClusters(); @@ -42,7 +41,7 @@ public class CpuPowerCalculator extends PowerCalculator { for (int speed = 0; speed < speedsForCluster; speed++) { final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); final double cpuSpeedStepPower = timeUs * - mProfile.getAveragePowerForCpu(cluster, speed); + mProfile.getAveragePowerForCpuCore(cluster, speed); if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + speed + " timeUs=" + timeUs + " power=" @@ -51,6 +50,25 @@ public class CpuPowerCalculator extends PowerCalculator { cpuPowerMaUs += cpuSpeedStepPower; } } + cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower( + PowerProfile.POWER_CPU_ACTIVE); + long[] cpuClusterTimes = u.getCpuClusterTimes(); + if (cpuClusterTimes != null) { + if (cpuClusterTimes.length == numClusters) { + for (int i = 0; i < numClusters; i++) { + double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i); + cpuPowerMaUs += power; + if (DEBUG) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs=" + + cpuClusterTimes[i] + " power=" + + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR)); + } + } + } else { + Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # " + + numClusters + " actual # " + cpuClusterTimes.length); + } + } app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java new file mode 100644 index 000000000000..cb96c5cdfb60 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java @@ -0,0 +1,146 @@ +/* + * 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.internal.os; + +import android.annotation.Nullable; +import android.os.StrictMode; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +/** + * Reads /proc/uid_concurrent_active_time which has the format: + * active: X (X is # cores) + * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) + * [uid1]: [time-0] [time-1] [time-2] ... ... + * ... + * Time-N means the CPU time a UID spent running concurrently with N other processes. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a + * proper delta. + */ +public class KernelUidCpuActiveTimeReader { + private static final boolean DEBUG = false; + private static final String TAG = "KernelUidCpuActiveTimeReader"; + private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time"; + + private int mCoreCount; + private long mLastTimeReadMs; + private long mNowTimeMs; + private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>(); + + public interface Callback { + void onUidCpuActiveTime(int uid, long cpuActiveTimeMs); + } + + public void readDelta(@Nullable Callback cb) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { + mNowTimeMs = SystemClock.elapsedRealtime(); + readDeltaInternal(reader, cb); + mLastTimeReadMs = mNowTimeMs; + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + public void removeUid(int uid) { + mLastUidCpuActiveTimeMs.delete(uid); + } + + public void removeUidsInRange(int startUid, int endUid) { + if (endUid < startUid) { + Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); + return; + } + mLastUidCpuActiveTimeMs.put(startUid, null); + mLastUidCpuActiveTimeMs.put(endUid, null); + final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid); + final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid); + mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); + } + + @VisibleForTesting + public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { + String line = reader.readLine(); + if (line == null || !line.startsWith("active:")) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + if (mCoreCount == 0) { + mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1)); + } + while ((line = reader.readLine()) != null) { + final int index = line.indexOf(' '); + final int uid = Integer.parseInt(line.substring(0, index - 1), 10); + readTimesForUid(uid, line.substring(index + 1), cb); + } + } + + private void readTimesForUid(int uid, String line, @Nullable Callback cb) { + long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid); + if (lastActiveTime == null) { + lastActiveTime = new long[mCoreCount]; + mLastUidCpuActiveTimeMs.put(uid, lastActiveTime); + } + final String[] timesStr = line.split(" "); + if (timesStr.length != mCoreCount) { + Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d", + timesStr.length, mCoreCount)); + return; + } + long sumDeltas = 0; + final long[] curActiveTime = new long[mCoreCount]; + boolean notify = false; + for (int i = 0; i < mCoreCount; i++) { + // Times read will be in units of 10ms + curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10; + long delta = curActiveTime[i] - lastActiveTime[i]; + if (delta < 0 || curActiveTime[i] < 0) { + if (DEBUG) { + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("Malformed cpu active time for UID=%d\n", uid)); + sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i])); + sb.append("times=("); + TimeUtils.formatDuration(mLastTimeReadMs, sb); + sb.append(","); + TimeUtils.formatDuration(mNowTimeMs, sb); + sb.append(")"); + Slog.e(TAG, sb.toString()); + } + return; + } + notify |= delta > 0; + sumDeltas += delta / (i + 1); + } + if (notify) { + System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount); + if (cb != null) { + cb.onUidCpuActiveTime(uid, sumDeltas); + } + } + } +} diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java new file mode 100644 index 000000000000..85153bc45d07 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java @@ -0,0 +1,178 @@ +/* + * 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.internal.os; + +import android.annotation.Nullable; +import android.os.StrictMode; +import android.os.SystemClock; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Reads /proc/uid_concurrent_policy_time which has the format: + * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) + * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... + * ... + * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a proper + * delta. + */ +public class KernelUidCpuClusterTimeReader { + + private static final boolean DEBUG = false; + private static final String TAG = "KernelUidCpuClusterTimeReader"; + private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time"; + + // mCoreOnCluster[i] is the # of cores on cluster i + private int[] mCoreOnCluster; + private int mCores; + private long mLastTimeReadMs; + private long mNowTimeMs; + private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>(); + + public interface Callback { + /** + * @param uid + * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of + * processes running concurrently with this uid. + */ + void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs); + } + + public void readDelta(@Nullable Callback cb) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) { + mNowTimeMs = SystemClock.elapsedRealtime(); + readDeltaInternal(reader, cb); + mLastTimeReadMs = mNowTimeMs; + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } + + public void removeUid(int uid) { + mLastUidPolicyTimeMs.delete(uid); + } + + public void removeUidsInRange(int startUid, int endUid) { + if (endUid < startUid) { + Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid); + return; + } + mLastUidPolicyTimeMs.put(startUid, null); + mLastUidPolicyTimeMs.put(endUid, null); + final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid); + final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid); + mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); + } + + @VisibleForTesting + public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException { + String line = reader.readLine(); + if (line == null || !line.startsWith("policy")) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + if (mCoreOnCluster == null) { + List<Integer> list = new ArrayList<>(); + String[] policies = line.split(" "); + + if (policies.length == 0 || policies.length % 2 != 0) { + Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE)); + return; + } + + for (int i = 0; i < policies.length; i+=2) { + list.add(Integer.parseInt(policies[i+1])); + } + + mCoreOnCluster = new int[list.size()]; + for(int i=0;i<list.size();i++){ + mCoreOnCluster[i] = list.get(i); + mCores += mCoreOnCluster[i]; + } + } + while ((line = reader.readLine()) != null) { + final int index = line.indexOf(' '); + final int uid = Integer.parseInt(line.substring(0, index - 1), 10); + readTimesForUid(uid, line.substring(index + 1), cb); + } + } + + private void readTimesForUid(int uid, String line, @Nullable Callback cb) { + long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid); + if (lastPolicyTime == null) { + lastPolicyTime = new long[mCores]; + mLastUidPolicyTimeMs.put(uid, lastPolicyTime); + } + final String[] timeStr = line.split(" "); + if (timeStr.length != mCores) { + Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d", + timeStr.length, mCores)); + return; + } + final long[] deltaPolicyTime = new long[mCores]; + final long[] currPolicyTime = new long[mCores]; + boolean notify = false; + for (int i = 0; i < mCores; i++) { + // Times read will be in units of 10ms + currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10; + deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i]; + if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) { + if (DEBUG) { + final StringBuilder sb = new StringBuilder(); + sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid)); + sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i])); + sb.append("times=("); + TimeUtils.formatDuration(mLastTimeReadMs, sb); + sb.append(","); + TimeUtils.formatDuration(mNowTimeMs, sb); + sb.append(")"); + Slog.e(TAG, sb.toString()); + } + return; + } + notify |= deltaPolicyTime[i] > 0; + } + if (notify) { + System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores); + if (cb != null) { + final long[] times = new long[mCoreOnCluster.length]; + int core = 0; + for (int i = 0; i < mCoreOnCluster.length; i++) { + for (int j = 0; j < mCoreOnCluster[i]; j++) { + times[i] += deltaPolicyTime[core++] / (j+1); + } + } + cb.onUidCpuPolicyTime(uid, times); + } + } + } +} diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 872b465a9ca5..fcbbcd036740 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -43,23 +44,25 @@ public class PowerProfile { public static final String POWER_NONE = "none"; /** - * Power consumption when CPU is in power collapse mode. + * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode. + * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should + * be zero on devices that can go into full CPU power collapse even when a wake + * lock is held. Otherwise, this is the power consumption in addition to + * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity. + * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters + * and cores. + * + * CPU Power Equation (assume two clusters): + * Total power = POWER_CPU_SUSPEND (always added) + * + POWER_CPU_IDLE (skip this and below if in power collapse mode) + * + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock + * is held) + * + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running) + * + core_power.cluster0 * num running cores in cluster 0 + * + core_power.cluster1 * num running cores in cluster 1 */ + public static final String POWER_CPU_SUSPEND = "cpu.suspend"; public static final String POWER_CPU_IDLE = "cpu.idle"; - - /** - * Power consumption when CPU is awake (when a wake lock is held). This - * should be 0 on devices that can go into full CPU power collapse even - * when a wake lock is held. Otherwise, this is the power consumption in - * addition to POWER_CPU_IDLE due to a wake lock being held but with no - * CPU activity. - */ - public static final String POWER_CPU_AWAKE = "cpu.awake"; - - /** - * Power consumption when CPU is in power collapse mode. - */ - @Deprecated public static final String POWER_CPU_ACTIVE = "cpu.active"; /** @@ -182,9 +185,6 @@ public class PowerProfile { */ public static final String POWER_CAMERA = "camera.avg"; - @Deprecated - public static final String POWER_CPU_SPEEDS = "cpu.speeds"; - /** * Power consumed by wif batched scaning. Broken down into bins by * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels @@ -197,7 +197,15 @@ public class PowerProfile { */ public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; - static final HashMap<String, Object> sPowerMap = new HashMap<>(); + /** + * A map from Power Use Item to its power consumption. + */ + static final HashMap<String, Double> sPowerItemMap = new HashMap<>(); + /** + * A map from Power Use Item to an array of its power consumption + * (for items with variable power e.g. CPU). + */ + static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>(); private static final String TAG_DEVICE = "device"; private static final String TAG_ITEM = "item"; @@ -207,23 +215,32 @@ public class PowerProfile { private static final Object sLock = new Object(); + @VisibleForTesting public PowerProfile(Context context) { - // Read the XML file for the given profile (normally only one per - // device) + this(context, false); + } + + /** + * For PowerProfileTest + */ + @VisibleForTesting + public PowerProfile(Context context, boolean forTest) { + // Read the XML file for the given profile (normally only one per device) synchronized (sLock) { - if (sPowerMap.size() == 0) { - readPowerValuesFromXml(context); + if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { + readPowerValuesFromXml(context, forTest); } initCpuClusters(); } } - private void readPowerValuesFromXml(Context context) { - int id = com.android.internal.R.xml.power_profile; + private void readPowerValuesFromXml(Context context, boolean forTest) { + final int id = forTest ? com.android.internal.R.xml.power_profile_test : + com.android.internal.R.xml.power_profile; final Resources resources = context.getResources(); XmlResourceParser parser = resources.getXml(id); boolean parsingArray = false; - ArrayList<Double> array = new ArrayList<Double>(); + ArrayList<Double> array = new ArrayList<>(); String arrayName = null; try { @@ -237,7 +254,7 @@ public class PowerProfile { if (parsingArray && !element.equals(TAG_ARRAYITEM)) { // Finish array - sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); parsingArray = false; } if (element.equals(TAG_ARRAY)) { @@ -255,7 +272,7 @@ public class PowerProfile { } catch (NumberFormatException nfe) { } if (element.equals(TAG_ITEM)) { - sPowerMap.put(name, value); + sPowerItemMap.put(name, value); } else if (parsingArray) { array.add(value); } @@ -263,7 +280,7 @@ public class PowerProfile { } } if (parsingArray) { - sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); } } catch (XmlPullParserException e) { throw new RuntimeException(e); @@ -300,52 +317,56 @@ public class PowerProfile { String key = configResIdKeys[i]; // if we already have some of these parameters in power_profile.xml, ignore the // value in config.xml - if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) { + if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) { continue; } int value = resources.getInteger(configResIds[i]); if (value > 0) { - sPowerMap.put(key, (double) value); + sPowerItemMap.put(key, (double) value); } } } private CpuClusterKey[] mCpuClusters; - private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; - private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster"; - private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster"; + private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; + private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster"; + private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster"; + private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster"; - @SuppressWarnings("deprecation") private void initCpuClusters() { - // Figure out how many CPU clusters we're dealing with - final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT); - if (obj == null || !(obj instanceof Double[])) { + if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { + final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT); + mCpuClusters = new CpuClusterKey[data.length]; + for (int cluster = 0; cluster < data.length; cluster++) { + int numCpusInCluster = (int) Math.round(data[cluster]); + mCpuClusters[cluster] = new CpuClusterKey( + CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster, + CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster); + } + } else { // Default to single. mCpuClusters = new CpuClusterKey[1]; - mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1); - - } else { - final Double[] array = (Double[]) obj; - mCpuClusters = new CpuClusterKey[array.length]; - for (int cluster = 0; cluster < array.length; cluster++) { - int numCpusInCluster = (int) Math.round(array[cluster]); - mCpuClusters[cluster] = new CpuClusterKey( - POWER_CPU_CLUSTER_SPEED_PREFIX + cluster, - POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster, - numCpusInCluster); + int numCpus = 1; + if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { + numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT)); } + mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0, + CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus); } } public static class CpuClusterKey { - private final String timeKey; - private final String powerKey; + private final String freqKey; + private final String clusterPowerKey; + private final String corePowerKey; private final int numCpus; - private CpuClusterKey(String timeKey, String powerKey, int numCpus) { - this.timeKey = timeKey; - this.powerKey = powerKey; + private CpuClusterKey(String freqKey, String clusterPowerKey, + String corePowerKey, int numCpus) { + this.freqKey = freqKey; + this.clusterPowerKey = clusterPowerKey; + this.corePowerKey = corePowerKey; this.numCpus = numCpus; } } @@ -354,21 +375,30 @@ public class PowerProfile { return mCpuClusters.length; } - public int getNumCoresInCpuCluster(int index) { - return mCpuClusters[index].numCpus; + public int getNumCoresInCpuCluster(int cluster) { + return mCpuClusters[cluster].numCpus; } - public int getNumSpeedStepsInCpuCluster(int index) { - Object value = sPowerMap.get(mCpuClusters[index].timeKey); - if (value != null && value instanceof Double[]) { - return ((Double[])value).length; + public int getNumSpeedStepsInCpuCluster(int cluster) { + if (cluster < 0 || cluster >= mCpuClusters.length) { + return 0; // index out of bound + } + if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) { + return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length; } return 1; // Only one speed } - public double getAveragePowerForCpu(int cluster, int step) { + public double getAveragePowerForCpuCluster(int cluster) { if (cluster >= 0 && cluster < mCpuClusters.length) { - return getAveragePower(mCpuClusters[cluster].powerKey, step); + return getAveragePower(mCpuClusters[cluster].clusterPowerKey); + } + return 0; + } + + public double getAveragePowerForCpuCore(int cluster, int step) { + if (cluster >= 0 && cluster < mCpuClusters.length) { + return getAveragePower(mCpuClusters[cluster].corePowerKey, step); } return 0; } @@ -379,14 +409,10 @@ public class PowerProfile { * @return the number of memory bandwidth buckets. */ public int getNumElements(String key) { - if (sPowerMap.containsKey(key)) { - Object data = sPowerMap.get(key); - if (data instanceof Double[]) { - final Double[] values = (Double[]) data; - return values.length; - } else { - return 1; - } + if (sPowerItemMap.containsKey(key)) { + return 1; + } else if (sPowerArrayMap.containsKey(key)) { + return sPowerArrayMap.get(key).length; } return 0; } @@ -399,13 +425,10 @@ public class PowerProfile { * @return the average current in milliAmps. */ public double getAveragePowerOrDefault(String type, double defaultValue) { - if (sPowerMap.containsKey(type)) { - Object data = sPowerMap.get(type); - if (data instanceof Double[]) { - return ((Double[])data)[0]; - } else { - return (Double) sPowerMap.get(type); - } + if (sPowerItemMap.containsKey(type)) { + return sPowerItemMap.get(type); + } else if (sPowerArrayMap.containsKey(type)) { + return sPowerArrayMap.get(type)[0]; } else { return defaultValue; } @@ -429,19 +452,16 @@ public class PowerProfile { * @return the average current in milliAmps. */ public double getAveragePower(String type, int level) { - if (sPowerMap.containsKey(type)) { - Object data = sPowerMap.get(type); - if (data instanceof Double[]) { - final Double[] values = (Double[]) data; - if (values.length > level && level >= 0) { - return values[level]; - } else if (level < 0 || values.length == 0) { - return 0; - } else { - return values[values.length - 1]; - } + if (sPowerItemMap.containsKey(type)) { + return sPowerItemMap.get(type); + } else if (sPowerArrayMap.containsKey(type)) { + final Double[] values = sPowerArrayMap.get(type); + if (values.length > level && level >= 0) { + return values[level]; + } else if (level < 0 || values.length == 0) { + return 0; } else { - return (Double) data; + return values[values.length - 1]; } } else { return 0; diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index c7897b2bbc3b..486b5842400c 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -26,7 +26,7 @@ public class WakelockPowerCalculator extends PowerCalculator { private long mTotalAppWakelockTimeMs = 0; public WakelockPowerCalculator(PowerProfile profile) { - mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE); + mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE); } @Override diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index cbc63cf813cb..079ba0bbe906 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -67,6 +67,9 @@ public final class Zygote { private Zygote() {} + /** Called for some security initialization before any fork. */ + native static void nativeSecurityInit(); + /** * Forks a new VM instance. The current VM must have been started * with the -Xzygote flag. <b>NOTE: new instance keeps all diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f814ba9e484d..2671f2947530 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -30,7 +30,6 @@ import android.os.IInstalld; import android.os.Environment; import android.os.Process; import android.os.RemoteException; -import android.os.Seccomp; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; @@ -781,12 +780,11 @@ public class ZygoteInit { // Zygote. Trace.setTracingEnabled(false, 0); + Zygote.nativeSecurityInit(); + // Zygote process unmounts root storage spaces. Zygote.nativeUnmountStorageOnInit(); - // Set seccomp policy - Seccomp.setPolicy(); - ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 5ec90941aa88..e097362a3fe8 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -55,7 +55,7 @@ oneway interface IStatusBar boolean showImeSwitcher); void setWindowState(int window, int state); - void showRecentApps(boolean triggeredFromAltTab, boolean fromHome); + void showRecentApps(boolean triggeredFromAltTab); void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); void toggleRecentApps(); void toggleSplitScreen(); @@ -115,7 +115,7 @@ oneway interface IStatusBar /** * Notifies the status bar that a new rotation suggestion is available. */ - void onProposedRotationChanged(int rotation); + void onProposedRotationChanged(int rotation, boolean isValid); /** * Set whether the top app currently hides the statusbar. diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 7b023f412cbc..621619c5134d 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -619,6 +619,10 @@ public class ArrayUtils { return size - leftIdx; } + public static @NonNull int[] defeatNullable(@Nullable int[] val) { + return (val != null) ? val : EmptyArray.INT; + } + public static @NonNull String[] defeatNullable(@Nullable String[] val) { return (val != null) ? val : EmptyArray.STRING; } diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java index e35f9f45acfe..e08eb587ab97 100644 --- a/core/java/com/android/internal/util/ConcurrentUtils.java +++ b/core/java/com/android/internal/util/ConcurrentUtils.java @@ -18,11 +18,13 @@ package com.android.internal.util; import android.os.Process; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -86,4 +88,27 @@ public class ConcurrentUtils { } } + /** + * Waits for {@link CountDownLatch#countDown()} to be called on the {@param countDownLatch}. + * <p>If {@link CountDownLatch#countDown()} doesn't occur within {@param timeoutMs}, this + * method will throw {@code IllegalStateException} + * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread + * and throw {@code IllegalStateException} + * + * @param countDownLatch the CountDownLatch which {@link CountDownLatch#countDown()} is + * being waited on. + * @param timeoutMs the maximum time waited for {@link CountDownLatch#countDown()} + * @param description a short description of the operation + */ + public static void waitForCountDownNoInterrupt(CountDownLatch countDownLatch, long timeoutMs, + String description) { + try { + if (!countDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) { + throw new IllegalStateException(description + " timed out."); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(description + " interrupted."); + } + } } diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java index 66b777e8e8e6..2b5103377ecb 100644 --- a/core/java/com/android/internal/util/DumpUtils.java +++ b/core/java/com/android/internal/util/DumpUtils.java @@ -102,6 +102,7 @@ public final class DumpUtils { case android.os.Process.ROOT_UID: case android.os.Process.SYSTEM_UID: case android.os.Process.SHELL_UID: + case android.os.Process.INCIDENTD_UID: return true; } diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index f7ea7875c8df..7fd94c6859fb 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,5 +1,6 @@ package com.android.internal.util; +import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -32,8 +33,18 @@ public class ScreenshotHelper { mContext = context; } + /** + * Request a screenshot be taken. + * + * @param screenshotType The type of screenshot, for example either + * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not. + * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not. + * @param handler A handler used in case the screenshot times out + */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, - final boolean hasNav, Handler handler) { + final boolean hasNav, @NonNull Handler handler) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index b2bab6f7d58f..e3f1f472ce5e 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -21,7 +21,7 @@ import android.app.trust.IStrongAuthTracker; import android.os.Bundle; import android.security.keystore.WrappedApplicationKey; import android.security.keystore.KeychainSnapshot; -import android.security.keystore.KeychainProtectionParameter; +import android.security.keystore.KeychainProtectionParams; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.VerifyCredentialResponse; @@ -60,7 +60,7 @@ interface ILockSettings { in byte[] token, int requestedQuality, int userId); void unlockUserWithToken(long tokenHandle, in byte[] token, int userId); - // Keystore RecoveryManager methods. + // Keystore RecoveryController methods. // {@code ServiceSpecificException} may be thrown to signal an error, which caller can // convert to {@code RecoveryManagerException}. void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList); @@ -75,10 +75,11 @@ interface ILockSettings { void setRecoverySecretTypes(in int[] secretTypes); int[] getRecoverySecretTypes(); int[] getPendingRecoverySecretTypes(); - void recoverySecretAvailable(in KeychainProtectionParameter recoverySecret); + void recoverySecretAvailable(in KeychainProtectionParams recoverySecret); byte[] startRecoverySession(in String sessionId, in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge, - in List<KeychainProtectionParameter> secrets); + in List<KeychainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); + void closeSession(in String sessionId); } diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index 20f05e61687f..33977f33fe75 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -60,6 +60,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mFirstLayout; private boolean mIsHidingAnimated; private boolean mNeedsGeneratedAvatar; + private Notification.Person mSender; public MessagingGroup(@NonNull Context context) { super(context); @@ -89,6 +90,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void setSender(Notification.Person sender) { + mSender = sender; mSenderName.setText(sender.getName()); mNeedsGeneratedAvatar = sender.getIcon() == null; if (!mNeedsGeneratedAvatar) { @@ -355,7 +357,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return 0; } - public View getSender() { + public View getSenderView() { return mSenderName; } @@ -370,4 +372,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public boolean needsGeneratedAvatar() { return mNeedsGeneratedAvatar; } + + public Notification.Person getSender() { + return mSender; + } } diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 834c93a7a98f..7a64cad463c8 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -35,7 +35,6 @@ import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.RemotableViewMethod; -import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -135,9 +134,24 @@ public class MessagingLayout extends FrameLayout { if (headerText != null) { mConversationTitle = headerText.getText(); } + addRemoteInputHistoryToMessages(newMessages, + extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY)); bind(newMessages, newHistoricMessages); } + private void addRemoteInputHistoryToMessages( + List<Notification.MessagingStyle.Message> newMessages, + CharSequence[] remoteInputHistory) { + if (remoteInputHistory == null || remoteInputHistory.length == 0) { + return; + } + for (int i = remoteInputHistory.length - 1; i >= 0; i--) { + CharSequence message = remoteInputHistory[i]; + newMessages.add(new Notification.MessagingStyle.Message( + message, 0, (Notification.Person) null)); + } + } + private void bind(List<Notification.MessagingStyle.Message> newMessages, List<Notification.MessagingStyle.Message> newHistoricMessages) { @@ -189,9 +203,10 @@ public class MessagingLayout extends FrameLayout { for (int i = 0; i < mGroups.size(); i++) { // Let's now set the avatars MessagingGroup group = mGroups.get(i); + boolean isOwnMessage = group.getSender() == mUser; CharSequence senderName = group.getSenderName(); if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName) - || (mIsOneToOne && mLargeIcon != null)) { + || (mIsOneToOne && mLargeIcon != null && !isOwnMessage)) { continue; } String symbol = uniqueNames.get(senderName); @@ -209,7 +224,7 @@ public class MessagingLayout extends FrameLayout { if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { continue; } - if (mIsOneToOne && mLargeIcon != null) { + if (mIsOneToOne && mLargeIcon != null && group.getSender() != mUser) { group.setAvatar(mLargeIcon); } else { Icon cachedIcon = cachedAvatars.get(senderName); @@ -271,6 +286,12 @@ public class MessagingLayout extends FrameLayout { public void setUser(Notification.Person user) { mUser = user; + if (mUser.getIcon() == null) { + Icon userIcon = Icon.createWithResource(getContext(), + com.android.internal.R.drawable.messaging_user); + userIcon.setTint(mLayoutColor); + mUser.setIcon(userIcon); + } } private void addMessagesToGroups(List<MessagingMessage> historicMessages, @@ -410,7 +431,7 @@ public class MessagingLayout extends FrameLayout { continue; } MessagingPropertyAnimator.fadeIn(group.getAvatar()); - MessagingPropertyAnimator.fadeIn(group.getSender()); + MessagingPropertyAnimator.fadeIn(group.getSenderView()); MessagingPropertyAnimator.startLocalTranslationFrom(group, group.getHeight(), LINEAR_OUT_SLOW_IN); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 96f3308ec178..543acc7c2158 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -97,7 +97,6 @@ cc_library_shared { "android_os_MessageQueue.cpp", "android_os_Parcel.cpp", "android_os_SELinux.cpp", - "android_os_seccomp.cpp", "android_os_SharedMemory.cpp", "android_os_SystemClock.cpp", "android_os_SystemProperties.cpp", @@ -120,6 +119,7 @@ cc_library_shared { "android_util_jar_StrictJarFile.cpp", "android_graphics_Canvas.cpp", "android_graphics_Picture.cpp", + "android/graphics/AnimatedImageDrawable.cpp", "android/graphics/Bitmap.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/ByteBufferStreamAdaptor.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6569b4783e67..aa9a82415f97 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -63,6 +63,7 @@ extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); @@ -166,7 +167,6 @@ extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); extern int register_android_os_VintfObject(JNIEnv *env); extern int register_android_os_VintfRuntimeInfo(JNIEnv *env); -extern int register_android_os_seccomp(JNIEnv* env); extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_Trace(JNIEnv* env); @@ -1397,6 +1397,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_ImageDecoder), + REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_Matrix), @@ -1427,7 +1428,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_GraphicsEnvironment), REG_JNI(register_android_os_MessageQueue), REG_JNI(register_android_os_SELinux), - REG_JNI(register_android_os_seccomp), REG_JNI(register_android_os_Trace), REG_JNI(register_android_os_UEventObserver), REG_JNI(register_android_net_LocalSocketImpl), diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp new file mode 100644 index 000000000000..12feaab5c684 --- /dev/null +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -0,0 +1,154 @@ +/* + * 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. + */ + +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "core_jni_helpers.h" + +#include <hwui/Canvas.h> +#include <SkAndroidCodec.h> +#include <SkAnimatedImage.h> +#include <SkColorFilter.h> +#include <SkPicture.h> +#include <SkPictureRecorder.h> + +using namespace android; + +struct AnimatedImageDrawable { + sk_sp<SkAnimatedImage> mDrawable; + SkPaint mPaint; +}; + +// Note: jpostProcess holds a handle to the ImageDecoder. +static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, + jlong nativeImageDecoder, jobject jpostProcess, + jint width, jint height, jobject jsubset) { + if (nativeImageDecoder == 0) { + doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!"); + return 0; + } + + auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder); + auto info = imageDecoder->mCodec->getInfo(); + const SkISize scaledSize = SkISize::Make(width, height); + SkIRect subset; + if (jsubset) { + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + } else { + subset = SkIRect::MakeWH(width, height); + } + + sk_sp<SkPicture> picture; + if (jpostProcess) { + SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); + + SkPictureRecorder recorder; + SkCanvas* skcanvas = recorder.beginRecording(bounds); + std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas)); + postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(), + bounds.height()); + if (env->ExceptionCheck()) { + return 0; + } + picture = recorder.finishRecordingAsPicture(); + } + + std::unique_ptr<AnimatedImageDrawable> drawable(new AnimatedImageDrawable); + drawable->mDrawable = SkAnimatedImage::Make(std::move(imageDecoder->mCodec), + scaledSize, subset, std::move(picture)); + if (!drawable->mDrawable) { + doThrowIOE(env, "Failed to create drawable"); + return 0; + } + drawable->mDrawable->start(); + + return reinterpret_cast<jlong>(drawable.release()); +} + +static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) { + delete drawable; +} + +static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct)); +} + +static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong canvasPtr, jlong msecs) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + double timeToNextUpdate = drawable->mDrawable->update(msecs); + auto* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->drawAnimatedImage(drawable->mDrawable.get(), 0, 0, &drawable->mPaint); + return (jlong) timeToNextUpdate; +} + +static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint alpha) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mPaint.setAlpha(alpha); +} + +static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->mPaint.getAlpha(); +} + +static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong nativeFilter) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter); + drawable->mPaint.setColorFilter(sk_ref_sp(filter)); +} + +static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->mDrawable->isRunning(); +} + +static void AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mDrawable->start(); +} + +static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mDrawable->stop(); +} + +static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + // FIXME: Report the size of the internal SkBitmap etc. + return sizeof(drawable); +} + +static const JNINativeMethod gAnimatedImageDrawableMethods[] = { + { "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate }, + { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, + { "nDraw", "(JJJ)J", (void*) AnimatedImageDrawable_nDraw }, + { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha }, + { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha }, + { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, + { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, + { "nStart", "(J)V", (void*) AnimatedImageDrawable_nStart }, + { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, + { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, +}; + +int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", + gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); +} + diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 79aa5acac4ee..685fcaf15211 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -237,10 +237,22 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, // Create the codec. NinePatchPeeker peeker; - std::unique_ptr<SkAndroidCodec> codec = SkAndroidCodec::MakeFromStream( - std::move(stream), &peeker); - if (!codec.get()) { - return nullObjectReturn("SkAndroidCodec::MakeFromStream returned null"); + std::unique_ptr<SkAndroidCodec> codec; + { + SkCodec::Result result; + std::unique_ptr<SkCodec> c = SkCodec::MakeFromStream(std::move(stream), &result, + &peeker); + if (!c) { + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + return nullObjectReturn(msg.c_str()); + } + + codec = SkAndroidCodec::MakeFromCodec(std::move(c)); + if (!codec) { + return nullObjectReturn("SkAndroidCodec::MakeFromCodec returned null"); + } } // Do not allow ninepatch decodes to 565. In the past, decodes to 565 diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 7f4b384235e5..dcb81fa623f4 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -66,10 +66,6 @@ public: static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap); static SkRegion* getNativeRegion(JNIEnv*, jobject region); - // Given the 'native' long held by the Rasterizer.java object, return a - // ref to its SkRasterizer* (or NULL). - static sk_sp<SkRasterizer> refNativeRasterizer(jlong rasterizerHandle); - /* * LegacyBitmapConfig is the old enum in Skia that matched the enum int values * in Bitmap.Config. Skia no longer supports this config, but has replaced it diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index a0a4be4590be..1c2528ffecd4 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -19,12 +19,11 @@ #include "ByteBufferStreamAdaptor.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" -#include "NinePatchPeeker.h" +#include "ImageDecoder.h" #include "Utils.h" #include "core_jni_helpers.h" #include <hwui/Bitmap.h> -#include <hwui/Canvas.h> #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> @@ -43,53 +42,51 @@ static jclass gIncomplete_class; static jclass gCorrupt_class; static jclass gCanvas_class; static jmethodID gImageDecoder_constructorMethodID; +static jmethodID gImageDecoder_postProcessMethodID; static jmethodID gPoint_constructorMethodID; static jmethodID gIncomplete_constructorMethodID; static jmethodID gCorrupt_constructorMethodID; static jmethodID gCallback_onPartialImageMethodID; -static jmethodID gPostProcess_postProcessMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_releaseMethodID; -struct ImageDecoder { - // These need to stay in sync with ImageDecoder.java's Allocator constants. - enum Allocator { - kDefault_Allocator = 0, - kSoftware_Allocator = 1, - kSharedMemory_Allocator = 2, - kHardware_Allocator = 3, - }; - - // These need to stay in sync with PixelFormat.java's Format constants. - enum PixelFormat { - kUnknown = 0, - kTranslucent = -3, - kOpaque = -1, - }; - - NinePatchPeeker mPeeker; - std::unique_ptr<SkAndroidCodec> mCodec; -}; - static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { if (!stream.get()) { doThrowIOE(env, "Failed to create a stream"); return nullptr; } std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); - decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker); - if (!decoder->mCodec.get()) { - // FIXME: (b/71578461) Use the error message from - // SkCodec::MakeFromStream to report a more informative error message. - doThrowIOE(env, "Failed to create an SkCodec"); + SkCodec::Result result; + auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get()); + if (!codec) { + switch (result) { + case SkCodec::kIncompleteInput: + env->ThrowNew(gIncomplete_class, "Incomplete input"); + break; + default: + SkString msg; + msg.printf("Failed to create image decoder with message '%s'", + SkCodec::ResultToString(result)); + doThrowIOE(env, msg.c_str()); + break; + + } return nullptr; } + // FIXME: Avoid parsing the whole image? + const bool animated = codec->getFrameCount() > 1; + decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); + if (!decoder->mCodec.get()) { + doThrowIOE(env, "Could not create AndroidCodec"); + return nullptr; + } const auto& info = decoder->mCodec->getInfo(); const int width = info.width(); const int height = info.height(); return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID, - reinterpret_cast<jlong>(decoder.release()), width, height); + reinterpret_cast<jlong>(decoder.release()), width, height, + animated); } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, @@ -160,11 +157,24 @@ static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jby return native_create(env, std::move(stream)); } -static bool supports_any_down_scale(const SkAndroidCodec* codec) { - return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas, + int width, int height) { + jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, + reinterpret_cast<jlong>(canvas.get())); + if (!jcanvas) { + doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); + return ImageDecoder::kUnknown; + } + + // jcanvas now owns canvas. + canvas.release(); + + return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, + jcanvas, width, height); } -// This method should never return null. Instead, it should throw an exception. +// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr +// otherwise. static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, @@ -173,33 +183,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong jboolean asAlphaMask) { auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); SkAndroidCodec* codec = decoder->mCodec.get(); - SkImageInfo decodeInfo = codec->getInfo(); - bool scale = false; - int sampleSize = 1; - if (desiredWidth != decodeInfo.width() || desiredHeight != decodeInfo.height()) { - bool match = false; - if (desiredWidth < decodeInfo.width() && desiredHeight < decodeInfo.height()) { - if (supports_any_down_scale(codec)) { - match = true; - decodeInfo = decodeInfo.makeWH(desiredWidth, desiredHeight); - } else { - int sampleX = decodeInfo.width() / desiredWidth; - int sampleY = decodeInfo.height() / desiredHeight; - sampleSize = std::min(sampleX, sampleY); - SkISize sampledSize = codec->getSampledDimensions(sampleSize); - decodeInfo = decodeInfo.makeWH(sampledSize.width(), sampledSize.height()); - if (decodeInfo.width() == desiredWidth && decodeInfo.height() == desiredHeight) { - match = true; - } - } - } - if (!match) { - scale = true; - if (requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) { - doThrowISE(env, "Cannot scale unpremultiplied pixels!"); - return nullptr; - } - } + const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight); + SkISize decodeSize = desiredSize; + const int sampleSize = codec->computeSampleSize(&decodeSize); + const bool scale = desiredSize != decodeSize; + SkImageInfo decodeInfo = codec->getInfo().makeWH(decodeSize.width(), decodeSize.height()); + if (scale && requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) { + doThrowISE(env, "Cannot scale unpremultiplied pixels!"); + return nullptr; } switch (decodeInfo.alphaType()) { @@ -330,23 +321,23 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong // Ignore ninepatch when post-processing. if (!jpostProcess) { // FIXME: Share more code with BitmapFactory.cpp. - if (decoder->mPeeker.mPatch != nullptr) { + if (decoder->mPeeker->mPatch != nullptr) { if (scale) { - decoder->mPeeker.scale(scaleX, scaleY, desiredWidth, desiredHeight); + decoder->mPeeker->scale(scaleX, scaleY, desiredWidth, desiredHeight); } - size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize(); + size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == nullptr) { doThrowOOME(env, "Failed to allocate nine patch chunk."); return nullptr; } - env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize, - reinterpret_cast<jbyte*>(decoder->mPeeker.mPatch)); + env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize, + reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch)); } - if (decoder->mPeeker.mHasInsets) { - ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f); + if (decoder->mPeeker->mHasInsets) { + ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f); if (ninePatchInsets == nullptr) { doThrowOOME(env, "Failed to allocate nine patch insets."); return nullptr; @@ -402,28 +393,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong canvas.drawBitmap(bm, 0.0f, 0.0f, &paint); bm.swap(scaledBm); - nativeBitmap = scaledPixelRef; + nativeBitmap = std::move(scaledPixelRef); } if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); - jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, - reinterpret_cast<jlong>(canvas.get())); - if (!jcanvas) { - doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); - return nullptr; - } - // jcanvas will now own canvas. - canvas.release(); - jint pixelFormat = env->CallIntMethod(jpostProcess, gPostProcess_postProcessMethodID, - jcanvas, bm.width(), bm.height()); - if (env->ExceptionCheck()) { - return nullptr; - } - - // The Canvas objects are no longer needed, and will not remain valid. - env->CallVoidMethod(jcanvas, gCanvas_releaseMethodID); + jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas), + bm.width(), bm.height()); if (env->ExceptionCheck()) { return nullptr; } @@ -501,7 +478,7 @@ static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlon static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject outPadding) { auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); - decoder->mPeeker.getPadding(env, outPadding); + decoder->mPeeker->getPadding(env, outPadding); } static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { @@ -519,7 +496,7 @@ static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, - { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", + { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, @@ -529,7 +506,8 @@ static const JNINativeMethod gImageDecoderMethods[] = { int register_android_graphics_ImageDecoder(JNIEnv* env) { gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder")); - gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JII)V"); + gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V"); + gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I"); gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point")); gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V"); @@ -543,9 +521,6 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener"); gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z"); - jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess"); - gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I"); - gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V"); diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h new file mode 100644 index 000000000000..2df71eb19528 --- /dev/null +++ b/core/jni/android/graphics/ImageDecoder.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include "NinePatchPeeker.h" + +#include <hwui/Canvas.h> + +#include <jni.h> + +class SkAndroidCodec; + +using namespace android; + +struct ImageDecoder { + // These need to stay in sync with ImageDecoder.java's Allocator constants. + enum Allocator { + kDefault_Allocator = 0, + kSoftware_Allocator = 1, + kSharedMemory_Allocator = 2, + kHardware_Allocator = 3, + }; + + // These need to stay in sync with PixelFormat.java's Format constants. + enum PixelFormat { + kUnknown = 0, + kTranslucent = -3, + kOpaque = -1, + }; + + std::unique_ptr<SkAndroidCodec> mCodec; + sk_sp<NinePatchPeeker> mPeeker; + + ImageDecoder() + :mPeeker(new NinePatchPeeker) + {} +}; + +// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then +// releases the Canvas. +// Caller needs to check for exceptions. +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas, + int width, int height); diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index 1676d4b91e3d..888db32fdfac 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -27,6 +27,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> #include <ETC1/etc1.h> #include <SkBitmap.h> @@ -621,7 +622,7 @@ void util_multiplyMV(JNIEnv *env, jclass clazz, // --------------------------------------------------------------------------- -static int checkFormat(SkColorType colorType, int format, int type) +static int checkInternalFormat(SkColorType colorType, int format, int type) { switch(colorType) { case kN32_SkColorType: @@ -641,7 +642,7 @@ static int checkFormat(SkColorType colorType, int format, int type) } break; case kRGBA_F16_SkColorType: - if (type == GL_HALF_FLOAT_OES && format == PIXEL_FORMAT_RGBA_FP16) + if (type == GL_HALF_FLOAT && format == GL_RGBA16F) return 0; break; default: @@ -650,6 +651,20 @@ static int checkFormat(SkColorType colorType, int format, int type) return -1; } +// The internal format is no longer the same as pixel format, per Table 2 in +// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml +static int getPixelFormatFromInternalFormat(uint32_t internalFormat) { + switch (internalFormat) { + // For sized internal format. + case GL_RGBA16F: + return GL_RGBA; + // Base internal formats and pixel formats are still the same, see Table 1 in + // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml + default: + return internalFormat; + } +} + static int getInternalFormat(SkColorType colorType) { switch(colorType) { @@ -662,7 +677,7 @@ static int getInternalFormat(SkColorType colorType) case kRGB_565_SkColorType: return GL_RGB; case kRGBA_F16_SkColorType: - return PIXEL_FORMAT_RGBA_FP16; + return GL_RGBA16F; default: return -1; } @@ -680,7 +695,7 @@ static int getType(SkColorType colorType) case kRGB_565_SkColorType: return GL_UNSIGNED_SHORT_5_6_5; case kRGBA_F16_SkColorType: - return GL_HALF_FLOAT_OES; + return GL_HALF_FLOAT; default: return -1; } @@ -715,7 +730,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, if (type < 0) { type = getType(colorType); } - int err = checkFormat(colorType, internalformat, type); + int err = checkInternalFormat(colorType, internalformat, type); if (err) return err; const int w = bitmap.width(); @@ -724,7 +739,8 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, if (internalformat == GL_PALETTE8_RGBA8_OES) { err = -1; } else { - glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); + glTexImage2D(target, level, internalformat, w, h, border, + getPixelFormatFromInternalFormat(internalformat), type, p); } return err; } @@ -736,12 +752,13 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, SkBitmap bitmap; GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); SkColorType colorType = bitmap.colorType(); + int internalFormat = getInternalFormat(colorType); if (format < 0) { - format = getInternalFormat(colorType); + format = getPixelFormatFromInternalFormat(internalFormat); if (format == GL_PALETTE8_RGBA8_OES) return -1; // glCompressedTexSubImage2D() not supported } - int err = checkFormat(colorType, format, type); + int err = checkInternalFormat(colorType, internalFormat, type); if (err) return err; const int w = bitmap.width(); diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp index eefcb74e0f70..34544d37a6bf 100644 --- a/core/jni/android_database_SQLiteCommon.cpp +++ b/core/jni/android_database_SQLiteCommon.cpp @@ -18,8 +18,108 @@ #include <utils/String8.h> +#include <map> + namespace android { +static const std::map<int, std::string> sErrorCodesMap = { + // Primary Result Code List + {4, "SQLITE_ABORT"}, + {23, "SQLITE_AUTH"}, + {5, "SQLITE_BUSY"}, + {14, "SQLITE_CANTOPEN"}, + {19, "SQLITE_CONSTRAINT"}, + {11, "SQLITE_CORRUPT"}, + {101, "SQLITE_DONE"}, + {16, "SQLITE_EMPTY"}, + {1, "SQLITE_ERROR"}, + {24, "SQLITE_FORMAT"}, + {13, "SQLITE_FULL"}, + {2, "SQLITE_INTERNAL"}, + {9, "SQLITE_INTERRUPT"}, + {10, "SQLITE_IOERR"}, + {6, "SQLITE_LOCKED"}, + {20, "SQLITE_MISMATCH"}, + {21, "SQLITE_MISUSE"}, + {22, "SQLITE_NOLFS"}, + {7, "SQLITE_NOMEM"}, + {26, "SQLITE_NOTADB"}, + {12, "SQLITE_NOTFOUND"}, + {27, "SQLITE_NOTICE"}, + {0, "SQLITE_OK"}, + {3, "SQLITE_PERM"}, + {15, "SQLITE_PROTOCOL"}, + {25, "SQLITE_RANGE"}, + {8, "SQLITE_READONLY"}, + {100, "SQLITE_ROW"}, + {17, "SQLITE_SCHEMA"}, + {18, "SQLITE_TOOBIG"}, + {28, "SQLITE_WARNING"}, + // Extended Result Code List + {516, "SQLITE_ABORT_ROLLBACK"}, + {261, "SQLITE_BUSY_RECOVERY"}, + {517, "SQLITE_BUSY_SNAPSHOT"}, + {1038, "SQLITE_CANTOPEN_CONVPATH"}, + {782, "SQLITE_CANTOPEN_FULLPATH"}, + {526, "SQLITE_CANTOPEN_ISDIR"}, + {270, "SQLITE_CANTOPEN_NOTEMPDIR"}, + {275, "SQLITE_CONSTRAINT_CHECK"}, + {531, "SQLITE_CONSTRAINT_COMMITHOOK"}, + {787, "SQLITE_CONSTRAINT_FOREIGNKEY"}, + {1043, "SQLITE_CONSTRAINT_FUNCTION"}, + {1299, "SQLITE_CONSTRAINT_NOTNULL"}, + {1555, "SQLITE_CONSTRAINT_PRIMARYKEY"}, + {2579, "SQLITE_CONSTRAINT_ROWID"}, + {1811, "SQLITE_CONSTRAINT_TRIGGER"}, + {2067, "SQLITE_CONSTRAINT_UNIQUE"}, + {2323, "SQLITE_CONSTRAINT_VTAB"}, + {267, "SQLITE_CORRUPT_VTAB"}, + {3338, "SQLITE_IOERR_ACCESS"}, + {2826, "SQLITE_IOERR_BLOCKED"}, + {3594, "SQLITE_IOERR_CHECKRESERVEDLOCK"}, + {4106, "SQLITE_IOERR_CLOSE"}, + {6666, "SQLITE_IOERR_CONVPATH"}, + {2570, "SQLITE_IOERR_DELETE"}, + {5898, "SQLITE_IOERR_DELETE_NOENT"}, + {4362, "SQLITE_IOERR_DIR_CLOSE"}, + {1290, "SQLITE_IOERR_DIR_FSYNC"}, + {1802, "SQLITE_IOERR_FSTAT"}, + {1034, "SQLITE_IOERR_FSYNC"}, + {6410, "SQLITE_IOERR_GETTEMPPATH"}, + {3850, "SQLITE_IOERR_LOCK"}, + {6154, "SQLITE_IOERR_MMAP"}, + {3082, "SQLITE_IOERR_NOMEM"}, + {2314, "SQLITE_IOERR_RDLOCK"}, + {266, "SQLITE_IOERR_READ"}, + {5642, "SQLITE_IOERR_SEEK"}, + {5130, "SQLITE_IOERR_SHMLOCK"}, + {5386, "SQLITE_IOERR_SHMMAP"}, + {4618, "SQLITE_IOERR_SHMOPEN"}, + {4874, "SQLITE_IOERR_SHMSIZE"}, + {522, "SQLITE_IOERR_SHORT_READ"}, + {1546, "SQLITE_IOERR_TRUNCATE"}, + {2058, "SQLITE_IOERR_UNLOCK"}, + {778, "SQLITE_IOERR_WRITE"}, + {262, "SQLITE_LOCKED_SHAREDCACHE"}, + {539, "SQLITE_NOTICE_RECOVER_ROLLBACK"}, + {283, "SQLITE_NOTICE_RECOVER_WAL"}, + {256, "SQLITE_OK_LOAD_PERMANENTLY"}, + {520, "SQLITE_READONLY_CANTLOCK"}, + {1032, "SQLITE_READONLY_DBMOVED"}, + {264, "SQLITE_READONLY_RECOVERY"}, + {776, "SQLITE_READONLY_ROLLBACK"}, + {284, "SQLITE_WARNING_AUTOINDEX"}, +}; + +static std::string sqlite3_error_code_to_msg(int errcode) { + auto it = sErrorCodesMap.find(errcode); + if (it != sErrorCodesMap.end()) { + return std::to_string(errcode) + " " + it->second; + } else { + return std::to_string(errcode); + } +} + /* throw a SQLiteException with a message appropriate for the error in handle */ void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { throw_sqlite3_exception(env, handle, NULL); @@ -123,7 +223,8 @@ void throw_sqlite3_exception(JNIEnv* env, int errcode, if (sqlite3Message) { String8 fullMessage; fullMessage.append(sqlite3Message); - fullMessage.appendFormat(" (code %d)", errcode); // print extended error code + const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str(); + fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code if (message) { fullMessage.append(": "); fullMessage.append(message); diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 092aaf62bbc0..c79f5bd96e99 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -33,6 +33,9 @@ #define ENCODING_AAC_HE_V2 12 #define ENCODING_IEC61937 13 #define ENCODING_DOLBY_TRUEHD 14 +#define ENCODING_AAC_ELD 15 +#define ENCODING_AAC_XHE 16 +#define ENCODING_AC4 17 #define ENCODING_INVALID 0 #define ENCODING_DEFAULT 1 @@ -71,6 +74,12 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_DOLBY_TRUEHD; case ENCODING_IEC61937: return AUDIO_FORMAT_IEC61937; + case ENCODING_AAC_ELD: + return AUDIO_FORMAT_AAC_ELD; + case ENCODING_AAC_XHE: + return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC + case ENCODING_AC4: + return AUDIO_FORMAT_AC4; case ENCODING_DEFAULT: return AUDIO_FORMAT_DEFAULT; default: @@ -114,6 +123,13 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_IEC61937; case AUDIO_FORMAT_DOLBY_TRUEHD: return ENCODING_DOLBY_TRUEHD; + case AUDIO_FORMAT_AAC_ELD: + return ENCODING_AAC_ELD; + // FIXME needs addition of AUDIO_FORMAT_AAC_XHE + //case AUDIO_FORMAT_AAC_XHE: + // return ENCODING_AAC_XHE; + case AUDIO_FORMAT_AC4: + return ENCODING_AC4; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: @@ -121,6 +137,25 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) } } +// This function converts Java channel masks to a native channel mask. +// validity should be checked with audio_is_output_channel(). +static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks( + jint channelPositionMask, jint channelIndexMask) +{ + // 0 is the java android.media.AudioFormat.CHANNEL_INVALID value + if (channelIndexMask != 0) { // channel index mask takes priority + // To convert to a native channel mask, the Java channel index mask + // requires adding the index representation. + return audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_INDEX, + channelIndexMask); + } + // To convert to a native channel mask, the Java channel position mask + // requires a shift by 2 to skip the two deprecated channel + // configurations "default" and "mono". + return (audio_channel_mask_t)((uint32_t)channelPositionMask >> 2); +} + static inline audio_channel_mask_t outChannelMaskToNative(int channelMask) { switch (channelMask) { diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 7ec68edddf52..2be947158fc5 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1770,6 +1770,24 @@ android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, (audio_devices_t)device); } +static jboolean +android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz, + jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask) +{ + audio_offload_info_t format = AUDIO_INFO_INITIALIZER; + format.format = (audio_format_t) audioFormatToNative(encoding); + format.sample_rate = (uint32_t) sampleRate; + format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask); + format.stream_type = AUDIO_STREAM_MUSIC; + format.has_video = false; + format.is_streaming = false; + // offload duration unknown at this point: + // client side code cannot access "audio.offload.min.duration.secs" property to make a query + // agnostic of duration, so using acceptable estimate of 2mn + format.duration_us = 120 * 1000000; + return AudioSystem::isOffloadSupported(format); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -1823,6 +1841,7 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_AudioSystem_registerRecordingCallback}, {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, + {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported}, }; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 556ac27dfe9e..11011b1d7c16 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -73,6 +73,7 @@ struct audiotrack_callback_cookie { jobject audioTrack_ref; bool busy; Condition cond; + bool isOffload; }; // keep these values in sync with AudioTrack.java @@ -90,6 +91,7 @@ class AudioTrackJniStorage { AudioTrackJniStorage() { mCallbackData.audioTrack_class = 0; mCallbackData.audioTrack_ref = 0; + mCallbackData.isOffload = false; } ~AudioTrackJniStorage() { @@ -132,27 +134,34 @@ static void audioCallback(int event, void* user, void *info) { } switch (event) { - case AudioTrack::EVENT_MARKER: { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (user != NULL && env != NULL) { - env->CallStaticVoidMethod( - callbackInfo->audioTrack_class, - javaAudioTrackFields.postNativeEventInJava, - callbackInfo->audioTrack_ref, event, 0,0, NULL); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); + // Offload only events + case AudioTrack::EVENT_STREAM_END: + case AudioTrack::EVENT_MORE_DATA: + // a.k.a. tear down + case AudioTrack::EVENT_NEW_IAUDIOTRACK: + if (callbackInfo->isOffload) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (user != NULL && env != NULL) { + env->CallStaticVoidMethod( + callbackInfo->audioTrack_class, + javaAudioTrackFields.postNativeEventInJava, + callbackInfo->audioTrack_ref, event, 0,0, NULL); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } } - } } break; + // PCM and offload events + case AudioTrack::EVENT_MARKER: case AudioTrack::EVENT_NEW_POS: { JNIEnv *env = AndroidRuntime::getJNIEnv(); if (user != NULL && env != NULL) { env->CallStaticVoidMethod( - callbackInfo->audioTrack_class, - javaAudioTrackFields.postNativeEventInJava, - callbackInfo->audioTrack_ref, event, 0,0, NULL); + callbackInfo->audioTrack_class, + javaAudioTrackFields.postNativeEventInJava, + callbackInfo->audioTrack_ref, event, 0,0, NULL); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); @@ -198,30 +207,12 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio return getAudioTrack(env, audioTrackObj); } -// This function converts Java channel masks to a native channel mask. -// validity should be checked with audio_is_output_channel(). -static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks( - jint channelPositionMask, jint channelIndexMask) -{ - if (channelIndexMask != 0) { // channel index mask takes priority - // To convert to a native channel mask, the Java channel index mask - // requires adding the index representation. - return audio_channel_mask_from_representation_and_bits( - AUDIO_CHANNEL_REPRESENTATION_INDEX, - channelIndexMask); - } - // To convert to a native channel mask, the Java channel position mask - // requires a shift by 2 to skip the two deprecated channel - // configurations "default" and "mono". - return (audio_channel_mask_t)(channelPositionMask >> 2); -} - // ---------------------------------------------------------------------------- static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa, jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask, jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, - jlong nativeAudioTrack) { + jlong nativeAudioTrack, jboolean offload) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d" "nativeAudioTrack=0x%" PRIX64, @@ -322,8 +313,19 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioTrack object can be garbage collected. lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); + lpJniStorage->mCallbackData.isOffload = offload; lpJniStorage->mCallbackData.busy = false; + audio_offload_info_t offloadInfo; + if (offload) { + offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.format = format; + offloadInfo.sample_rate = sampleRateInHertz; + offloadInfo.channel_mask = nativeChannelMask; + offloadInfo.has_video = false; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload + } + // initialize the native AudioTrack object status_t status = NO_ERROR; switch (memoryMode) { @@ -342,7 +344,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job true,// thread can call Java sessionId,// audio session ID AudioTrack::TRANSFER_SYNC, - NULL, // default offloadInfo + offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values paa); break; @@ -1234,7 +1236,7 @@ static const JNINativeMethod gMethods[] = { {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I", + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I", (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index 9494fb8e7eef..061349aee96f 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -391,6 +391,10 @@ static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) { Status status; status_t err = ::android::hardware::readFromParcel(&status, *parcel); signalExceptionForError(env, err); + + if (!status.isOk()) { + signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); + } } static void JHwParcel_native_release( diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp deleted file mode 100644 index 06e2a167de0a..000000000000 --- a/core/jni/android_os_seccomp.cpp +++ /dev/null @@ -1,47 +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. - */ - -#include "core_jni_helpers.h" -#include <nativehelper/JniConstants.h> -#include "utils/Log.h" -#include <selinux/selinux.h> - -#include "seccomp_policy.h" - -static void Seccomp_setPolicy(JNIEnv* /*env*/) { - if (security_getenforce() == 0) { - ALOGI("seccomp disabled by setenforce 0"); - return; - } - - if (!set_seccomp_filter()) { - ALOGE("Failed to set seccomp policy - killing"); - exit(1); - } -} - -static const JNINativeMethod method_table[] = { - NATIVE_METHOD(Seccomp, setPolicy, "()V"), -}; - -namespace android { - -int register_android_os_seccomp(JNIEnv* env) { - return android::RegisterMethodsOrDie(env, "android/os/Seccomp", - method_table, NELEM(method_table)); -} - -} diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp index bdae0b22987f..58c05b44ed5c 100644 --- a/core/jni/android_text_MeasuredParagraph.cpp +++ b/core/jni/android_text_MeasuredParagraph.cpp @@ -85,12 +85,12 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong // Regular JNI static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr, - jcharArray javaText) { + jcharArray javaText, jboolean computeHyphenation) { ScopedCharArrayRO text(env, javaText); const minikin::U16StringPiece textBuffer(text.get(), text.size()); // Pass the ownership to Java. - return toJLong(toBuilder(builderPtr)->build(textBuffer).release()); + return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation).release()); } // Regular JNI @@ -108,7 +108,7 @@ static const JNINativeMethod gMethods[] = { {"nInitBuilder", "()J", (void*) nInitBuilder}, {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, - {"nBuildNativeMeasuredParagraph", "(J[C)J", (void*) nBuildNativeMeasuredParagraph}, + {"nBuildNativeMeasuredParagraph", "(J[CZ)J", (void*) nBuildNativeMeasuredParagraph}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, // MeasuredParagraph native functions. diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp index 0cb69359901d..d254de65f765 100644 --- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp +++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp @@ -45,6 +45,7 @@ static struct { jfieldID tag; jfieldID metered; jfieldID roaming; + jfieldID defaultNetwork; jfieldID rxBytes; jfieldID rxPackets; jfieldID txBytes; @@ -246,6 +247,9 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, ScopedIntArrayRW roaming(env, get_int_array(env, stats, gNetworkStatsClassInfo.roaming, size, grow)); if (roaming.get() == NULL) return -1; + ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats, + gNetworkStatsClassInfo.defaultNetwork, size, grow)); + if (defaultNetwork.get() == NULL) return -1; ScopedLongArrayRW rxBytes(env, get_long_array(env, stats, gNetworkStatsClassInfo.rxBytes, size, grow)); if (rxBytes.get() == NULL) return -1; @@ -269,7 +273,7 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, uid[i] = lines[i].uid; set[i] = lines[i].set; tag[i] = lines[i].tag; - // Metered and Roaming are populated in Java-land by inspecting the iface properties. + // Metered, roaming and defaultNetwork are populated in Java-land. rxBytes[i] = lines[i].rxBytes; rxPackets[i] = lines[i].rxPackets; txBytes[i] = lines[i].txBytes; @@ -285,6 +289,8 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray()); env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray()); env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray()); + env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork, + defaultNetwork.getJavaArray()); env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray()); env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray()); env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray()); @@ -318,6 +324,7 @@ int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) { gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I"); gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I"); gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I"); + gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I"); gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J"); gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J"); gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J"); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 1407ae4c4bfe..13e6fcdc8426 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -53,6 +53,7 @@ #include <private/android_filesystem_config.h> #include <utils/String8.h> #include <selinux/android.h> +#include <seccomp_policy.h> #include <processgroup/processgroup.h> #include "core_jni_helpers.h" @@ -76,6 +77,8 @@ static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; static jclass gZygoteClass; static jmethodID gCallPostForkChildHooks; +static bool g_is_security_enforced = true; + // Must match values in com.android.internal.os.Zygote. enum MountExternalKind { MOUNT_EXTERNAL_NONE = 0, @@ -229,6 +232,20 @@ static void PreApplicationInit() { mallopt(M_DECAY_TIME, 1); } +static void SetUpSeccompFilter(uid_t uid) { + if (!g_is_security_enforced) { + ALOGI("seccomp disabled by setenforce 0"); + return; + } + + // Apply system or app filter based on uid. + if (getuid() >= AID_APP_START) { + set_app_seccomp_filter(); + } else { + set_system_seccomp_filter(); + } +} + static void EnableKeepCapabilities(JNIEnv* env) { int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); if (rc == -1) { @@ -541,6 +558,11 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed."); } + // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to + // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see + // b/71859146). + SetUpSeccompFilter(uid); + // Keep capabilities across UID change, unless we're staying root. if (uid != 0) { EnableKeepCapabilities(env); @@ -698,6 +720,12 @@ static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) { namespace android { +static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) { + // security_getenforce is not allowed on app process. Initialize and cache the value before + // zygote forks. + g_is_security_enforced = security_getenforce(); +} + static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) { PreApplicationInit(); } @@ -835,6 +863,8 @@ static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* en } static const JNINativeMethod gMethods[] = { + { "nativeSecurityInit", "()V", + (void *) com_android_internal_os_Zygote_nativeSecurityInit }, { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I", (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize }, diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 2752a7e07ff4..e5efb90c922e 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -21,7 +21,6 @@ option java_outer_classname = "IncidentProtoMetadata"; import "frameworks/base/core/proto/android/os/batterytype.proto"; import "frameworks/base/core/proto/android/os/cpufreq.proto"; import "frameworks/base/core/proto/android/os/cpuinfo.proto"; -import "frameworks/base/core/proto/android/os/incidentheader.proto"; import "frameworks/base/core/proto/android/os/kernelwake.proto"; import "frameworks/base/core/proto/android/os/pagetypeinfo.proto"; import "frameworks/base/core/proto/android/os/procrank.proto"; @@ -46,6 +45,7 @@ import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; +import "frameworks/base/libs/incident/proto/android/os/header.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/libs/incident/proto/android/section.proto"; diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index d4bdb9b577dc..8d6df12d56dd 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -389,8 +389,10 @@ message GlobalSettingsProto { optional SettingProto notification_snooze_options = 341; optional SettingProto enable_gnss_raw_meas_full_tracking = 346; optional SettingProto zram_enabled = 347; + optional SettingProto enable_smart_replies_in_notifications = 348; + optional SettingProto show_first_crash_dialog = 349; - // Next tag = 348; + // Next tag = 350; } message SecureSettingsProto { @@ -592,8 +594,9 @@ message SecureSettingsProto { optional SettingProto qs_auto_added_tiles = 193; optional SettingProto lockdown_in_power_menu = 194; optional SettingProto backup_manager_constants = 169; + optional SettingProto show_first_crash_dialog_dev_option = 195; - // Next tag = 195 + // Next tag = 196 } message SystemSettingsProto { diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto index f725e8a8c7da..3c7a0e3ba475 100644 --- a/core/proto/android/service/diskstats.proto +++ b/core/proto/android/service/diskstats.proto @@ -43,6 +43,8 @@ message DiskStatsServiceDumpProto { optional EncryptionType encryption = 5; // Cached values of folder sizes, etc. optional DiskStatsCachedValuesProto cached_folder_sizes = 6; + // Average write speed of storaged benchmark for last 24 hours + optional int32 benchmarked_write_speed_kbps = 7; } message DiskStatsCachedValuesProto { diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto index 23613fdabf76..ad9191cac2e4 100644 --- a/core/proto/android/service/netstats.proto +++ b/core/proto/android/service/netstats.proto @@ -63,6 +63,8 @@ message NetworkIdentityProto { optional bool roaming = 4; optional bool metered = 5; + + optional bool default_network = 6; } // Corresponds to NetworkStatsRecorder. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cfd7c958a181..bf9d79bc75dd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -554,8 +554,6 @@ <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" /> <!-- Added in O --> - <!-- TODO: temporary broadcast used by AutoFillManagerServiceImpl; will be removed --> - <protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" /> <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" /> <protected-broadcast android:name="com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION" /> <protected-broadcast android:name="android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED" /> @@ -1749,6 +1747,12 @@ <permission android:name="android.permission.SEND_EMBMS_INTENTS" android:protectionLevel="signature|privileged" /> + + <!-- Allows internal management of the sensor framework + @hide --> + <permission android:name="android.permission.MANAGE_SENSORS" + android:protectionLevel="signature" /> + <!-- Must be required by an ImsService to ensure that only the system can bind to it. <p>Protection level: signature|privileged @@ -2689,6 +2693,13 @@ <permission android:name="android.permission.BIND_AUTOFILL_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by an {@link android.service.autofill.AutofillFieldClassificationService} + to ensure that only the system can bind to it. + @hide This is not a third-party API (intended for OEMs and system apps). + --> + <permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by hotword enrollment application, to ensure that only the system can interact with it. @hide <p>Not for use by third-party applications.</p> --> @@ -3186,10 +3197,14 @@ <permission android:name="android.permission.BIND_APPWIDGET" android:protectionLevel="signature|privileged" /> + <!-- @hide Allows sysui to manage user grants of slice permissions. --> + <permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" + android:protectionLevel="signature" /> + <!-- Allows an application to bind app's slices and get their content. This content will be surfaced to the user and not to leave the device. - <p>Not for use by third-party applications. --> + <p>Not for use by third-party applications.--> <permission android:name="android.permission.BIND_SLICE" android:protectionLevel="signature|privileged|development" /> @@ -3692,11 +3707,20 @@ <permission android:name="android.permission.READ_RUNTIME_PROFILES" android:protectionLevel="signature|privileged" /> + <!-- @hide Allows audio policy management. --> + <permission android:name="android.permission.MANAGE_AUDIO_POLICY" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to turn on / off quiet mode. @hide <p>Not for use by third-party applications. --> <permission android:name="android.permission.MODIFY_QUIET_MODE" android:protectionLevel="signature|privileged" /> + <!-- Allows internal management of the camera framework + @hide --> + <permission android:name="android.permission.MANAGE_CAMERA" + android:protectionLevel="signature" /> + <!-- Allows an application to control remote animations. See {@link ActivityOptions#makeRemoteAnimation} @hide <p>Not for use by third-party applications. --> diff --git a/core/res/res/drawable/messaging_user.xml b/core/res/res/drawable/messaging_user.xml new file mode 100644 index 000000000000..3137698ab90b --- /dev/null +++ b/core/res/res/drawable/messaging_user.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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:viewportHeight="192.0" + android:viewportWidth="192.0"> + <path android:fillColor="#000000" + android:fillAlpha="0.125" + android:pathData="M96,0C43.01,0 0,43.01 0,96s43.01,96 96,96s96,-43.01 96,-96S148.99,0 96,0z"/> + <path android:fillColor="#BDBDBD" + android:pathData="M96,85.09c13.28,0 24,-10.72 24,-24c0,-13.28 -10.72,-24 -24,-24s-24,10.72 -24,24C72,74.37 82.72,85.09 96,85.09z"/> + <path android:fillColor="#BDBDBD" + android:pathData="M96,99.27c-29.33,0 -52.36,14.18 -52.36,27.27c11.09,17.06 30.51,28.36 52.36,28.36s41.27,-11.3 52.36,-28.36C148.36,113.45 125.33,99.27 96,99.27z"/> +</vector> diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml index 528efca49fc3..a88836eff5a0 100644 --- a/core/res/res/layout/autofill_dataset_picker.xml +++ b/core/res/res/layout/autofill_dataset_picker.xml @@ -24,6 +24,8 @@ android:id="@+id/autofill_dataset_list" android:layout_width="fill_parent" android:layout_height="fill_parent" + android:drawSelectorOnTop="true" + android:clickable="true" android:divider="@null" android:visibility="gone"> </ListView> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 0a996cabdf2a..ee287c3c63ec 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"ድንገተኛ አደጋ"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"የሳንካ ሪፖርት"</string> <string name="global_action_logout" msgid="935179188218826050">"ክፍለ-ጊዜን አብቃ"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ቅጽበታዊ ገጽ እይታ"</string> <string name="bugreport_title" msgid="2667494803742548533">"የሳንካ ሪፖርት ውሰድ"</string> <string name="bugreport_message" msgid="398447048750350456">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"መስተጋብራዊ ሪፖርት"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi በራስ-ሰር ይበራል"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ከፍተኛ ጥራት ያለው የተቀመጠ አውታረ መረብ አቅራቢያ ሲሆኑ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"መልሰህ አታብራ"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi በራስ-ሰር በርቷል"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"ከተቀመጠ አውታረ መረብ አቅራቢያ ነዎት፦ <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"ወደ Wi-Fi አውታረ መረብ በመለያ ግባ"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ወደ አውታረ መረብ በመለያ ይግቡ"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"መተግበሪያ ምትኬን እና ወደ ነበረበት መመለስን ሳለማይደግፍ አቋራጭ ወደ ነበረበት ሊመለስ አልቻለም"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"በመተግበሪያ ፊርማ አለመዛመድ አቋራጭን ወደነበረበት መመለስ አልተቻለም"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"አቋራጭን ወደ ነበረበት መመለስ አልተቻለም"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"አቋራጭ ተሰናክሏል"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"አራግፍ"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"የሆነው ሆኖ አስጀምር"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"ጎጂ መተግበሪያ ይራገፍ?"</string> </resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 684c2574e475..6b5f1c8c4e28 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -221,6 +221,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Stav nouze"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string> <string name="global_action_logout" msgid="935179188218826050">"Ukončit relaci"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string> <string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string> <string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivní přehled"</string> @@ -1167,6 +1168,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se zapne automaticky"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Když budete v dosahu kvalitní uložené sítě"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Znovu nezapínat"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Automaticky se zapnulo připojení Wi-Fi"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Jste v dosahu uložené sítě: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Přihlásit se k síti Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Přihlásit se k síti"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1877,6 +1880,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Zkratku nelze obnovit, protože aplikace nepodporuje zálohování a obnovu"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Zkratku nelze obnovit, protože se neshoduje podpis aplikace"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Zkratku nelze obnovit"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Zkratka nefunguje"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Odinstalovat"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Přesto spustit"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Odinstalovat škodlivou aplikaci?"</string> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 9d317eb410ac..b94cc907a95a 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Notfall"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string> <string name="global_action_logout" msgid="935179188218826050">"Sitzung beenden"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string> <string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status deines Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte habe etwas Geduld."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiver Bericht"</string> @@ -379,7 +381,7 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Ermöglicht der App, die Anrufliste deines Tablets zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string> <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Ermöglicht der App, die Anrufliste deines Fernsehers zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string> <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Ermöglicht der App, die Anrufliste deines Telefons zu ändern, einschließlich der Daten über ein- und ausgehende Anrufe. Schädliche Apps können so deine Anrufliste löschen oder ändern."</string> - <string name="permlab_bodySensors" msgid="4683341291818520277">"Auf Körpersensoren wie z. B. Herzfrequenzmesser zugreifen"</string> + <string name="permlab_bodySensors" msgid="4683341291818520277">"Auf Körpersensoren wie z. B. Pulsmesser zugreifen"</string> <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ermöglicht der App, auf Daten von Sensoren zuzugreifen, die deine körperliche Verfassung überwachen, beispielsweise deinen Puls"</string> <string name="permlab_readCalendar" msgid="6716116972752441641">"Kalendertermine und Details lesen"</string> <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"Diese App kann alle auf deinem Tablet gespeicherten Kalendertermine lesen und deine Kalenderdaten teilen oder speichern."</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"WLAN wird automatisch aktiviert"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Wenn du in der Nähe eines sicheren gespeicherten Netzwerks bist"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nicht wieder aktivieren"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"In WLAN anmelden"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Im Netzwerk anmelden"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1813,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Verknüpfung konnte nicht wiederhergestellt werden, weil die App keine Sicherung und keine Wiederherstellung unterstützt"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Verknüpfung konnte nicht wiederhergestellt werden, weil die App-Signatur nicht übereinstimmt"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Verknüpfung konnte nicht wiederhergestellt werden"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Verknüpfung ist deaktiviert"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 700641edb4a1..2910067b309a 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string> <string name="global_action_logout" msgid="935179188218826050">"End session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string> <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string> </resources> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 700641edb4a1..2910067b309a 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string> <string name="global_action_logout" msgid="935179188218826050">"End session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string> <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 700641edb4a1..2910067b309a 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string> <string name="global_action_logout" msgid="935179188218826050">"End session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string> <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string> </resources> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 700641edb4a1..2910067b309a 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string> <string name="global_action_logout" msgid="935179188218826050">"End session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string> <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high‑quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to a Wi-Fi network"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string> </resources> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index ba21245b1f9a..5f7f19cbd56d 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string> <string name="global_action_logout" msgid="935179188218826050">"End session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string> <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an e-mail message. It will take a little time from starting the bug report until it is ready to be sent; please be patient."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive report"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi will turn on automatically"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"When you\'re near a high quality saved network"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Don\'t turn back on"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi turned on automatically"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"You\'re near a saved network: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Sign in to Wi-Fi network"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Sign in to network"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Couldn’t restore shortcut because app doesn’t support backup and restore"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Couldn’t restore shortcut because of app signature mismatch"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Couldn’t restore shortcut"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Shortcut is disabled"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstall"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Launch anyway"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstall harmful app?"</string> </resources> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 100ed1ef17fb..960dfd8495c3 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -86,7 +86,7 @@ <string name="NetworkPreferenceSwitchSummary" msgid="7056776609127756440">"Seinalea hobea izan dadin, aldatu sare mota Ezarpenak > Sareak eta Internet > Sare mugikorrak > Sare mota hobetsia atalean."</string> <string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"Wi‑Fi bidezko deiak aktibo daude"</string> <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"Sare mugikorrera konektatuta egon behar da larrialdi-deiak egin ahal izateko."</string> - <string name="notification_channel_network_alert" msgid="4427736684338074967">"Abisuak"</string> + <string name="notification_channel_network_alert" msgid="4427736684338074967">"Alertak"</string> <string name="notification_channel_call_forward" msgid="2419697808481833249">"Dei-desbideratzea"</string> <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Larrialdi-deiak soilik jasotzeko modua"</string> <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Datu mugikorren egoera"</string> @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Larrialdi-deiak"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Akatsen txostena"</string> <string name="global_action_logout" msgid="935179188218826050">"Amaitu saioa"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Sortu akatsen txostena"</string> <string name="bugreport_message" msgid="398447048750350456">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Txosten dinamikoa"</string> @@ -251,7 +253,7 @@ <string name="notification_channel_network_available" msgid="4531717914138179517">"Sare bat erabilgarri dago"</string> <string name="notification_channel_vpn" msgid="8330103431055860618">"VPN egoera"</string> <string name="notification_channel_device_admin" msgid="1568154104368069249">"Gailuen administrazioa"</string> - <string name="notification_channel_alerts" msgid="4496839309318519037">"Abisuak"</string> + <string name="notification_channel_alerts" msgid="4496839309318519037">"Alertak"</string> <string name="notification_channel_retail_mode" msgid="6088920674914038779">"Saltzaileentzako demoa"</string> <string name="notification_channel_usb" msgid="9006850475328924681">"USB konexioa"</string> <string name="notification_channel_heavy_weight_app" msgid="6218742927792852607">"Aplikazio bat abian da"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi konexioa automatikoki aktibatuko da"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gordeta daukazun kalitate handiko sare batetik gertu zaudenean"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ez aktibatu berriro"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Hasi saioa Wi-Fi sarean"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Hasi saioa sarean"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ezin izan da leheneratu lasterbidea aplikazioak ez duelako onartzen babeskopiak egiteko eta leheneratzeko aukera"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ezin izan da leheneratu lasterbidea aplikazioaren sinadurak ez datozelako bat"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ezin izan da leheneratu lasterbidea"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Desgaituta dago lasterbidea"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 9af3ed74eb8a..aa6608aeaa5c 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Urgence"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bogue"</string> <string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string> <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bogue"</string> <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme de courriel. Merci de patienter pendant la préparation du rapport de bogue. Cette opération peut prendre quelques instants."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi s\'activera automatiquement"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes près d\'un réseau enregistré de haute qualité"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi activé automatiquement"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vous êtes à proximité d\'un réseau enregistré : <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Connectez-vous au réseau Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Connectez-vous au réseau"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossible de restaurer le raccourci, car l\'application ne prend pas en charge la sauvegarde et la restauration"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossible de restaurer le raccourci en raison d\'une erreur de correspondance des signature d\'applications"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossible de restaurer le raccourci"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Le raccourci est désactivé"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Désinstaller"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lancer quand même"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Désinstaller l\'application nuisible?"</string> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 97cc0ec476f9..06a56845b7c4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Urgences"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string> <string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string> <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string> <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Le Wi-Fi sera activé automatiquement"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Lorsque vous êtes à proximité d\'un réseau enregistré de haute qualité"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne pas réactiver"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi activé automatiquement"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vous vous trouvez à proximité d\'un réseau enregistré : <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Connectez-vous au réseau Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Se connecter au réseau"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Le raccourci ne peut pas être restauré car l\'application n\'accepte pas la sauvegarde et la restauration"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Le raccourci ne peut pas être restauré car la signature de l\'application est différente"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossible de restaurer le raccourci"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Le raccourci est désactivé"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Désinstaller"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lancer quand même"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Désinstaller l\'application malveillante ?"</string> </resources> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 81e5f4f9bae0..2af95ce87057 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"કટોકટી"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"બગ રિપોર્ટ"</string> <string name="global_action_logout" msgid="935179188218826050">"સત્ર સમાપ્ત કરો"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"બગ રિપોર્ટ લો"</string> <string name="bugreport_message" msgid="398447048750350456">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"વાઇ-ફાઇ આપમેળે ચાલુ થઈ જશે"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"જ્યારે તમે એક ઉચ્ચ ક્વૉલિટીવાળા સાચવેલ નેટવર્કની નજીક હોવ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"પાછું ચાલુ કરશો નહીં"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"વાઇ-ફાઇ નેટવર્ક પર સાઇન ઇન કરો"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"નેટવર્ક પર સાઇન ઇન કરો"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપ બૅકઅપ અને ફરી મેળવવાનું સમર્થન કરતી નથી"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"શૉર્ટકટ ફરી મેળવી શકાયો નથી કારણ કે ઍપમાં છે તે સહી મેળ ખાતી નથી"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"શૉર્ટકટ પાછો મેળવી શકાયો નથી"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"શૉર્ટકટને બંધ કરવામાં આવ્યો છે"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 234958368279..251ec84b79b4 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"आपातकाल"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"गड़बड़ी की रिपोर्ट"</string> <string name="global_action_logout" msgid="935179188218826050">"सत्र खत्म करें"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"गड़बड़ी की रिपोर्ट लें"</string> <string name="bugreport_message" msgid="398447048750350456">"इससे ईमेल भेजने के लिए, आपके डिवाइस की मौजूदा स्थिति से जुड़ी जानकारी इकट्ठा की जाएगी. गड़बड़ी की रिपोर्ट बनना शुरू होने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया इंतज़ार करें."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"सहभागी रिपोर्ट"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाई-फ़ाई अपने आप चालू हो जाएगा"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"जब आप किसी अच्छी क्वालिटी वाले सेव किए गए नेटवर्क के पास हों"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"वापस चालू न करें"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"वाई-फ़ाई नेटवर्क में साइन इन करें"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"नेटवर्क में साइन इन करें"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1813,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"शॉर्टकट बहाल नहीं किया जा सका क्योंकि इस ऐप में बैकअप लेने और उसे बहाल करने की सुविधा नहीं है"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ऐप सिग्नेचर अलग होने के कारण शॉर्टकट बहाल नहीं किया जा सका"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट बहाल नहीं किया जा सका"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद है"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index cb2b8a5352d9..e3df0c87db9e 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string> <string name="global_action_logout" msgid="935179188218826050">"Akhiri sesi"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string> <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi akan aktif otomatis"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saat berada di dekat jaringan berkualitas tinggi yang tersimpan"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Jangan aktifkan kembali"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi diaktifkan otomatis"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Anda berada di dekat jaringan yang tersimpan: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Masuk ke jaringan Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Masuk ke jaringan"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Tidak dapat memulihkan pintasan karena aplikasi tidak mendukung backup dan pulihkan"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Tidak dapat memulihkan pintasan karena tanda tangan aplikasi tidak cocok"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Tidak dapat memulihkan pintasan."</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Pintasan dinonaktifkan"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Uninstal"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Tetap luncurkan"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Uninstal aplikasi berbahaya?"</string> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 3c9745f7500f..c1ef402b9036 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergenza"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Segnalazione di bug"</string> <string name="global_action_logout" msgid="935179188218826050">"Termina sessione"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Apri segnalazione bug"</string> <string name="bugreport_message" msgid="398447048750350456">"Verranno raccolte informazioni sullo stato corrente del dispositivo che saranno inviate sotto forma di messaggio email. Passerà un po\' di tempo prima che la segnalazione di bug aperta sia pronta per essere inviata; ti preghiamo di avere pazienza."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapporto interattivo"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Il Wi‑Fi verrà attivato automaticamente"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando ti trovi nell\'area di una rete salvata di alta qualità"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Non riattivare"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi attivato automaticamente"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ti trovi nell\'area di copertura di una rete salvata: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Accedi a rete Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Accedi alla rete"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Impossibile ripristinare la scorciatoia perché l\'app non supporta il backup e il ripristino"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Impossibile ripristinare la scorciatoia perché la firma dell\'app non corrisponde"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Impossibile ripristinare la scorciatoia"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"La scorciatoia è disattivata"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Disinstalla"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Avvia comunque"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Disinstallare l\'app dannosa?"</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 2fb35f8a5ac0..3b124fffe1ab 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"緊急通報"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"バグレポート"</string> <string name="global_action_logout" msgid="935179188218826050">"セッションを終了"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"スクリーンショット"</string> <string name="bugreport_title" msgid="2667494803742548533">"バグレポートを取得"</string> <string name="bugreport_message" msgid="398447048750350456">"現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"対話型レポート"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi は自動的にオンになります"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"高品質の保存済みネットワークの検出時"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"再度オンにしない"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi が自動的に ON になりました"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"保存済みネットワーク「<xliff:g id="NETWORK_SSID">%1$s</xliff:g>」の近くにいます"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fiネットワークにログイン"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ネットワークにログインしてください"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"このアプリはバックアップと復元に対応していないため、ショートカットを復元できませんでした"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"アプリの署名が一致しないため、ショートカットを復元できませんでした"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ショートカットを復元できませんでした"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ショートカットは無効になっています"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"アンインストール"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"このまま起動"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"有害なアプリをアンインストールしますか?"</string> </resources> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 2fe14dc9342a..b9539ea0a802 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"ತುರ್ತು"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"ದೋಷದ ವರದಿ"</string> <string name="global_action_logout" msgid="935179188218826050">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"ದೋಷ ವರದಿ ರಚಿಸಿ"</string> <string name="bugreport_message" msgid="398447048750350456">"ನಿಮ್ಮ ಸಾಧನದ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿಯ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಿಕೊಳ್ಳುವುದರ ಜೊತೆ ಇ-ಮೇಲ್ ರೂಪದಲ್ಲಿ ನಿಮಗೆ ರವಾನಿಸುತ್ತದೆ. ಇದು ದೋಷ ವರದಿಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಸಮಯದಿಂದ ಅದನ್ನು ಕಳುಹಿಸುವವರೆಗೆ ಸ್ವಲ್ಪ ಸಮಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ; ದಯವಿಟ್ಟು ತಾಳ್ಮೆಯಿಂದಿರಿ."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ಪರಸ್ಪರ ಸಂವಹನ ವರದಿ"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ವೈ‑ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗುತ್ತದೆ"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ನೀವು ಉಳಿಸಿದ ಅಧಿಕ ಗುಣಮಟ್ಟದ ನೆಟ್ವರ್ಕ್ ಸಮೀಪದಲ್ಲಿದ್ದಾಗ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ಮತ್ತೆ ಆನ್ ಮಾಡಲು ಹಿಂತಿರುಗಬೇಡಿ"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಕಪ್ ಮತ್ತು ಪುನಃಸ್ಥಾಪನೆಯನ್ನು ಬೆಂಬಲಿಸದಿರುವುದರಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ಅಪ್ಲಿಕೇಶನ್ ಸಹಿ ಹೊಂದಿಕೆಯಾಗದ ಕಾರಣದಿಂದ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಪುನಃ ಸ್ಥಾಪನೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ಶಾರ್ಟ್ಕಟ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 57745c273a0d..e248f45119c3 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -221,6 +221,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Skambutis pagalbos numeriu"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Pranešimas apie riktą"</string> <string name="global_action_logout" msgid="935179188218826050">"Baigti seansą"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrano kopija"</string> <string name="bugreport_title" msgid="2667494803742548533">"Pranešti apie riktą"</string> <string name="bugreport_message" msgid="398447048750350456">"Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie riktą bus paruoštas siųsti; būkite kantrūs."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interakt. ataskaita"</string> @@ -1167,6 +1168,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"„Wi‑Fi“ bus įjungtas automatiškai"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Kai būsite netoli išsaugoto aukštos kokybės tinklo"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Neįjunkite vėl"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"„Wi‑Fi“ įjungtas automatiškai"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Esate netoli išsaugoto tinklo: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Prisijungti prie „Wi-Fi“ tinklo"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Prisijungti prie tinklo"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1877,6 +1880,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nepavyko atkurti sparčiojo klavišo, nes programa nepalaiko atsarginės kopijos kūrimo ir atkūrimo funkcijų"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nepavyko atkurti sparčiojo klavišo, nes programos parašas neatitinka"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nepavyko atkurti sparčiojo klavišo"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Spartusis klavišas išjungtas"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Pašalinti"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Vis tiek paleisti"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Pašalinti žalingą programą?"</string> </resources> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 69c0fedac4ce..ed2dc0254126 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"അടിയന്തിരാവശ്യം"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string> <string name="global_action_logout" msgid="935179188218826050">"സെഷൻ അവസാനിപ്പിക്കുക"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"ബഗ് റിപ്പോർട്ട് എടുക്കുക"</string> <string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ഇന്റരാക്റ്റീവ് റിപ്പോർട്ട്"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"നിങ്ങൾ ഉയർന്ന നിലവാരമുള്ള സംരക്ഷിക്കപ്പെട്ട നെറ്റ്വർക്കിനരികിലെത്തുമ്പോൾ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"തിരികെ ഓണാക്കരുത്"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"വൈഫൈ നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ആപ്പ് \'ബാക്കപ്പും പുനഃസ്ഥാപിക്കലും\' പിന്തുണയ്ക്കാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ആപ്പ് സിഗ്നേച്ചർ പൊരുത്തപ്പെടാത്തതിനാൽ കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"കുറുക്കുവഴി പുനഃസ്ഥാപിക്കാനായില്ല"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"കുറുക്കുവഴി പ്രവർത്തനരഹിതമാണ്"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index addb2d18466f..9e36c1dd1616 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"आणीबाणी"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"बग रीपोर्ट"</string> <string name="global_action_logout" msgid="935179188218826050">"सेशन समाप्त करा"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"बग रीपोर्ट घ्या"</string> <string name="bugreport_message" msgid="398447048750350456">"ई-मेल संदेश म्हणून पाठविण्यासाठी, हे तुमच्या सद्य डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रीपोर्ट सुरू करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"परस्परसंवादी अहवाल"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"वाय-फाय आपोआप चालू होईल"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तुम्ही जेव्हा सेव्ह केलेल्या उच्च दर्जाच्या नेटवर्कजवळ असाल तेव्हा"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"पुन्हा चालू करू नका"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"वाय-फाय नेटवर्कमध्ये साइन इन करा"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"नेटवर्कवर साइन इन करा"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अॅप बॅकअप आणि रिस्टोअर करण्यास सपोर्ट देत नसल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अॅप स्वाक्षरी न जुळल्यामुळे शॉर्टकट रिस्टोअर करू शकलो नाही"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"शॉर्टकट रिस्टोअर करू शकलो नाही"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"शॉर्टकट बंद केलेला आहे"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 1777b26066d5..5f67641c7a78 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"အရေးပေါ်"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း"</string> <string name="global_action_logout" msgid="935179188218826050">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ဖန်သားပြင်ဓာတ်ပုံ"</string> <string name="bugreport_title" msgid="2667494803742548533">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းအား ယူရန်"</string> <string name="bugreport_message" msgid="398447048750350456">"သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"လက်ငင်းတုံ့ပြန်နိုင်သည့် အစီရင်ခံချက်"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ကို အလိုအလျောက် ပြန်ဖွင့်ပေးလိမ့်ပါမည်"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"သိမ်းဆည်းထားသည့် အရည်အသွေးမြင့်ကွန်ရက်များအနီးသို့ ရောက်ရှိသည့်အခါ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ပြန်မဖွင့်ပါနှင့်"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ကို အလိုအလျောက် ဖွင့်ထားသည်"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"သင်သည် သိမ်းထားသည့် ကွန်ရက်အနီးတွင် ရှိသည်− <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"ဝိုင်ဖိုင်ကွန်ရက်သို့ လက်မှတ်ထိုးဝင်ပါ"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1811,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"အက်ပ်သည် မိတ္တူကူးခြင်းနှင့် ပြန်ယူခြင်းကို ပံ့ပိုးခြင်းမရှိသည့်အတွက် ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"အက်ပ်လက်မှတ် မတူညီသည့်အတွက် ဖြတ်လမ်းလင့်ခ်များကို ပြန်ယူ၍မရပါ"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ဖြတ်လမ်းလင့်ခ်ကို ပြန်ယူ၍မရပါ"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ဖြတ်လမ်းလင့်ခ် ပိတ်ထားသည်"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"ဖြုတ်ရန်"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"မည်သို့ပင်ဖြစ်စေ ဖွင့်ရန်"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"အန္တရာယ်ရှိသည့် အက်ပ်ကို ဖြုတ်လိုပါသလား။"</string> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 873967a8d067..c3deb3e91335 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Nødsituasjon"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string> <string name="global_action_logout" msgid="935179188218826050">"Avslutt økten"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"Utfør feilrapport"</string> <string name="bugreport_message" msgid="398447048750350456">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv rapport"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi slås på automatisk"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Når du er i nærheten av et lagret nettverk av høy kvalitet"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ikke slå på igjen"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Logg på Wi-Fi-nettverket"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Logg på nettverk"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1813,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Kunne ikke gjenopprette snarveien fordi appen ikke støtter sikkerhetskopiering og gjenoppretting"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Kunne ikke gjenopprette snarveien på grunn av manglende samsvar for appsignaturen"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Kunne ikke gjenopprette snarveien"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Snarveien er slått av"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 3e0e04bdb555..71e1258b6ba4 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"आपतकालीन"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string> <string name="global_action_logout" msgid="935179188218826050">"सत्रको अन्त्य गर्नुहोस्"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट लिनुहोस्"</string> <string name="bugreport_message" msgid="398447048750350456">"एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"अन्तरक्रियामूलक रिपोर्ट"</string> @@ -1129,6 +1131,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi स्वतः सक्रिय हुनेछ"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"तपाईं कुनै सुरक्षित गरिएको उच्च गुणस्तरीय नेटवर्कको नजिक हुनुभएको अवस्थामा"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"फेरि सक्रिय नगर्नुहोला"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi नेटवर्कमा साइन इन गर्नुहोस्"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"सञ्जालमा साइन इन गर्नुहोस्"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1813,6 +1819,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"अनुप्रयोगले ब्याकअप तथा पुनर्स्थापनालाई समर्थन नगर्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"अनुप्रयोगमा प्रयोग गरिने हस्ताक्षर नमिल्ने हुँदा सर्टकट पुनर्स्थापित गर्न सकिएन"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"सर्टकट पुनर्स्थापित गर्न सकिएन"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"सर्टकट असक्षम पारिएको छ"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index b08b1b2e815f..8a11b9f3118a 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string> <string name="global_action_logout" msgid="935179188218826050">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"ਬਗ ਰਿਪੋਰਟ ਲਓ"</string> <string name="bugreport_message" msgid="398447048750350456">"ਇਹ ਇੱਕ ਈਮੇਲ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, ਤੁਹਾਡੇ ਵਰਤਮਾਨ ਡੀਵਾਈਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਠੀ ਕਰੇਗਾ। ਬੱਗ ਰਿਪੋਰਟ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਥੋੜ੍ਹਾ ਸਮਾਂ ਲੱਗੇਗਾ ਜਦੋਂ ਤੱਕ ਇਹ ਭੇਜੇ ਜਾਣ ਲਈ ਤਿਆਰ ਨਾ ਹੋਵੇ, ਕਿਰਪਾ ਕਰਕੇ ਧੀਰਜ ਰੱਖੋ।"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ਅੰਤਰਕਿਰਿਆਤਮਕ ਰਿਪੋਰਟ"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"ਵਾਈ‑ਫਾਈ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਚੱਲ ਪਵੇਗਾ"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਰੱਖਿਅਤ ਕੀਤੇ ਉੱਚ-ਗੁਣਵੱਤਾ ਵਾਲੇ ਨੈੱਟਵਰਕ ਦੇ ਨੇੜੇ ਹੋਵੋ"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"ਵਾਪਸ ਚਾਲੂ ਨਾ ਕਰੋ"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਐਪ \'ਬੈਕਅੱਪ ਅਤੇ ਮੁੜ-ਬਹਾਲੀ\' ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੀ"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ਐਪ ਹਸਤਾਖਰ ਦਾ ਮੇਲ ਨਾ ਹੋਣ ਕਾਰਨ ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"ਸ਼ਾਰਟਕੱਟ ਮੁੜ-ਬਹਾਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"ਸ਼ਾਰਟਕੱਟ ਬੰਦ ਕੀਤਾ ਹੋਇਆ ਹੈ"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 35cbd285003b..5201f1446052 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string> <string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string> <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string> <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Você está perto de uma rede salva: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Fazer login na rede Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Fazer login na rede"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Desinstalar app nocivo?"</string> </resources> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index a9a4a9115bdb..d6b888aaabfe 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string> <string name="global_action_logout" msgid="935179188218826050">"Terminar sessão"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string> <string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string> <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string> @@ -804,8 +805,8 @@ <string name="granularity_label_link" msgid="5815508880782488267">"link"</string> <string name="granularity_label_line" msgid="5764267235026120888">"linha"</string> <string name="factorytest_failed" msgid="5410270329114212041">"O teste de fábrica falhou"</string> - <string name="factorytest_not_system" msgid="4435201656767276723">"A acção FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string> - <string name="factorytest_no_action" msgid="872991874799998561">"Não foi localizado qualquer pacote que forneça a acção FACTORY_TEST."</string> + <string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string> + <string name="factorytest_no_action" msgid="872991874799998561">"Não foi localizado qualquer pacote que forneça a ação FACTORY_TEST."</string> <string name="factorytest_reboot" msgid="6320168203050791643">"Reiniciar"</string> <string name="js_dialog_title" msgid="1987483977834603872">"A página em \"<xliff:g id="TITLE">%s</xliff:g>\" indica:"</string> <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string> @@ -1023,7 +1024,7 @@ <string name="whichImageCaptureApplication" msgid="3680261417470652882">"Capturar imagem com"</string> <string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"Capturar imagem com o %1$s"</string> <string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"Capturar imagem"</string> - <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta acção."</string> + <string name="alwaysUse" msgid="4583018368000610438">"Utilizar por predefinição para esta ação."</string> <string name="use_a_different_app" msgid="8134926230585710243">"Utilizar outra aplicação"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Limpar a predefinição nas Definições do Sistema > Aplicações > Transferidas."</string> <string name="chooseActivity" msgid="7486876147751803333">"Escolha uma ação"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando estiver próximo de uma rede de alta qualidade guardada."</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não reativar"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Está perto de uma rede guardada: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>."</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Iniciar sessão na rede Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Início de sessão na rede"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque a aplicação não é compatível com a funcionalidade de cópia de segurança e restauro."</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido a uma falha de correspondência entre as assinaturas das aplicações."</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho."</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado."</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Pretende desinstalar a aplicação prejudicial?"</string> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 35cbd285003b..5201f1446052 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string> <string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string> <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string> <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"O Wi‑Fi será ativado automaticamente"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Quando você estiver perto de uma rede salva de alta qualidade"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Não ativar novamente"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ativado automaticamente"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Você está perto de uma rede salva: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Fazer login na rede Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Fazer login na rede"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Não foi possível restaurar o atalho porque o app não é compatível com backup e restauração"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Não foi possível restaurar o atalho devido à incompatibilidade de assinatura de apps"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Não foi possível restaurar o atalho"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"O atalho está desativado"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Desinstalar"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Iniciar mesmo assim"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Desinstalar app nocivo?"</string> </resources> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 010489412fbf..22b1bde06536 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -219,6 +219,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Urgență"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string> <string name="global_action_logout" msgid="935179188218826050">"Încheiați sesiunea"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captură de ecran"</string> <string name="bugreport_title" msgid="2667494803742548533">"Executați un raport despre erori"</string> <string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interactiv"</string> @@ -1145,6 +1146,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi se va activa automat"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Când vă aflați lângă o rețea salvată, de înaltă calitate"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Nu reactivați"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi-Fi s-a activat automat"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Vă aflați lângă o rețea salvată: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Conectați-vă la rețeaua Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Conectați-vă la rețea"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1842,6 +1845,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Nu s-a putut restabili comanda rapidă deoarece aplicația nu acceptă backupul și restabilirea"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Nu s-a putut restabili comanda rapidă din cauza nepotrivirii semnăturii aplicației"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Nu s-a putut restabili comanda rapidă"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Comanda rapidă este dezactivată"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Dezinstalați"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Lansați oricum"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Dezinstalați aplicația dăunătoare?"</string> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 460e0c75a72c..70dc651ded7a 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -221,6 +221,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Экстренный вызов"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string> <string name="global_action_logout" msgid="935179188218826050">"Закончить сеанс"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string> <string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string> <string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивный отчет"</string> @@ -1167,6 +1168,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi включится автоматически"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Когда вы будете в зоне действия сохраненной сети с хорошим сигналом."</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не включать снова"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi включен автоматически"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Вы находитесь в зоне действия сохраненной сети <xliff:g id="NETWORK_SSID">%1$s</xliff:g>."</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Подключение к Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Регистрация в сети"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1877,6 +1880,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не удалось восстановить ярлык: приложение не поддерживает резервное копирование"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не удалось восстановить ярлык: некорректная подпись приложения"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не удалось восстановить ярлык"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлык отключен"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Удалить"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Запустить все равно"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Удалите опасное приложение"</string> </resources> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 4188a4176fb7..609f99fdbda7 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"හදිසි"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"දෝෂ වර්තාව"</string> <string name="global_action_logout" msgid="935179188218826050">"සැසිය අවසන් කරන්න"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"තිර රුව"</string> <string name="bugreport_title" msgid="2667494803742548533">"දෝෂ වාර්තාවක් ගන්න"</string> <string name="bugreport_message" msgid="398447048750350456">"ඊ-තැපැල් පණිවිඩයක් ලෙස යැවීමට මෙය ඔබගේ වත්මන් උපාංග තත්වය ගැන තොරතුරු එකතු කරනු ඇත. දෝෂ වාර්තාව ආරම්භ කර එය යැවීමට සූදානම් කරන තෙක් එයට කිසියම් කාලයක් ගතවනු ඇත; කරුණාකර ඉවසන්න."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"අන්තර්ක්රියා වාර්."</string> @@ -1125,6 +1126,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi ස්වයංක්රියව ක්රියාත්මක වනු ඇත"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"ඔබ උසස් තත්ත්වයේ සුරැකි ජාලයක් අවට සිටින විට"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"නැවත ක්රියාත්මක නොකරන්න"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi ස්වයංක්රියව ක්රියාත්මක කරන ලදි"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"ඔබ සුරැකි ජාලයක් අවට සිටී: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi ජාලයට පුරනය වන්න"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"ජාලයට පුරනය වන්න"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1809,6 +1812,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"යෙදුම උපස්ථ සහ ප්රතිසාධනය සඳහා සහාය නොදක්වන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"යෙදුම් අත්සන නොගැළපෙන බැවින් කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"කෙටි මග ප්රතිසාධනය කළ නොහැකි විය"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"කෙටි මග අබල කර ඇත"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"අස්ථාපනය කරන්න"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"කෙසේ වුවත් ආරම්භ කරන්න"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"හානිකර යෙදුම අස්ථාපනය කරන්නද?"</string> </resources> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index df3c3e77990d..3c89e7563820 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -221,6 +221,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Klic v sili"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Poročilo o napakah"</string> <string name="global_action_logout" msgid="935179188218826050">"Končaj sejo"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Posnetek zaslona"</string> <string name="bugreport_title" msgid="2667494803742548533">"Ustvari poročilo o napakah"</string> <string name="bugreport_message" msgid="398447048750350456">"S tem bodo zbrani podatki o trenutnem stanju naprave, ki bodo poslani v e-poštnem sporočilu. Izvedba poročila o napakah in priprava trajata nekaj časa, zato bodite potrpežljivi."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivno poročilo"</string> @@ -1167,6 +1168,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Povezava Wi‑Fi se bo samodejno vklopila"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Ko ste v bližini zanesljivega shranjenega omrežja"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ne vklopi znova"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi je bil vklopljen samodejno"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ste v bližini shranjenega omrežja: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Prijavite se v omrežje Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Prijava v omrežje"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1877,6 +1880,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Bližnjice ni bilo mogoče obnoviti, ker aplikacija ne podpira varnostnega kopiranja in obnavljanja"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Bližnjice ni bilo mogoče obnoviti zaradi neujemanja podpisa aplikacije"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Bližnjice ni bilo mogoče obnoviti"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Bližnjica je onemogočena"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Odstrani"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Vseeno zaženi"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Želite odstraniti škodljivo aplikacijo?"</string> </resources> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index b98a5c7c5b7e..748f81288633 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -215,6 +215,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Dharura"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string> <string name="global_action_logout" msgid="935179188218826050">"Maliza kipindi"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Picha ya skrini"</string> <string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string> <string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ripoti ya kushirikiana"</string> @@ -1121,6 +1122,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi itawashwa kiotomatiki"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Unapokuwa karibu na mtandao uliohifadhiwa wenye ubora wa juu"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Usiwashe tena"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi imewashwa kiotomatiki"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Uko karibu na mtandao uliohifadhiwa: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Ingia kwa mtandao wa Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Ingia katika mtandao"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1805,6 +1808,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Imeshindwa kurejesha njia ya mkato kwa sababu programu haitumii kipengele cha hifadhi rudufu na kurejesha upya"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Imeshindwa kurejesha njia ya mkato kwa sababu ufunguo wako wa kuambatisha cheti kwenye programu haulingani"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Imeshindwa kurejesha njia ya mkato"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Njia ya mkato imezimwa"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Ondoa"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Fungua tu"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Ungependa kuondoa programu hatari?"</string> </resources> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index a04c5290052f..589ac91ba80f 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"అత్యవసరం"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"బగ్ నివేదిక"</string> <string name="global_action_logout" msgid="935179188218826050">"సెషన్ను ముగించు"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"బగ్ నివేదికను సిద్ధం చేయి"</string> <string name="bugreport_message" msgid="398447048750350456">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ప్రభావశీల నివేదిక"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi స్వయంచాలకంగా ఆన్ అవుతుంది"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"మీరు అధిక నాణ్యత గల సేవ్ చేసిన నెట్వర్క్కు సమీపంగా ఉన్నప్పుడు"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"తిరిగి ఆన్ చేయవద్దు"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"యాప్ సంతకం సరిపోలని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"షార్ట్కట్ నిలిపివేయబడింది"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 788444e99530..0d7095543a5f 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -221,6 +221,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string> <string name="global_action_logout" msgid="935179188218826050">"Завершити сеанс"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Знімок екрана"</string> <string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string> <string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтерактивний звіт"</string> @@ -1167,6 +1168,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi-Fi вмикатиметься автоматично"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Коли ви поблизу збереженої мережі високої якості"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Не вмикати знову"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"З’єднання Wi-Fi увімкнулось автоматично"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Ви поблизу збереженої мережі: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Вхід у мережу Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Вхід у мережу"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1877,6 +1880,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Не вдалося відновити ярлик, оскільки додаток не підтримує резервне копіювання та відновлення"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Не вдалося відновити ярлик, оскільки підписи додатків не збігаються"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Не вдалося відновити ярлик"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Ярлик вимкнено"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Видалити"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Усе одно запустити"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Видалити шкідливий додаток?"</string> </resources> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 38313d6e5982..54b25b71d65a 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -217,6 +217,8 @@ <string name="global_action_emergency" msgid="7112311161137421166">"ایمرجنسی"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"بگ کی اطلاع"</string> <string name="global_action_logout" msgid="935179188218826050">"سیشن ختم کریں"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="bugreport_title" msgid="2667494803742548533">"بگ کی اطلاع لیں"</string> <string name="bugreport_message" msgid="398447048750350456">"ایک ای میل پیغام کے بطور بھیجنے کیلئے، یہ آپ کے موجودہ آلہ کی حالت کے بارے میں معلومات جمع کرے گا۔ بگ کی اطلاع شروع کرنے سے لے کر بھیجنے کیلئے تیار ہونے تک اس میں تھوڑا وقت لگے گا؛ براہ کرم تحمل سے کام لیں۔"</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"متعامل رپورٹ"</string> @@ -1123,6 +1125,10 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi از خود آن ہو جائے گا"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"جب آپ اعلی معیار کے محفوظ کردہ نیٹ ورک کے قریب ہوں"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"دوبارہ آن نہ کریں"</string> + <!-- no translation found for wifi_wakeup_enabled_title (6534603733173085309) --> + <skip /> + <!-- no translation found for wifi_wakeup_enabled_content (189330154407990583) --> + <skip /> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi نیٹ ورک میں سائن ان کریں"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"نیٹ ورک میں سائن ان کریں"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1814,11 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"شارٹ کٹ کو بحال نہیں کیا جا سکا، کیونکہ ایپ بیک اپ اور بحالی کو سپورٹ نہیں کرتی ہے"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"ایپ دستخط غیر مماثل ہونے کی وجہ سے شارٹ کٹ کو بحال نہیں کیا جا سکا"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"شارٹ کٹ کو بحال نہیں کیا جا سکا"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"شارٹ کٹ غیر فعال ہے"</string> + <!-- no translation found for harmful_app_warning_uninstall (3846265696369136266) --> + <skip /> + <!-- no translation found for harmful_app_warning_launch_anyway (5784428382367400530) --> + <skip /> + <!-- no translation found for harmful_app_warning_title (2229996292333310435) --> <skip /> </resources> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 92ec3540008a..ce8613ff3842 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Favqulodda chaqiruv"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Nosozlik haqida ma’lumot berish"</string> <string name="global_action_logout" msgid="935179188218826050">"Seansni yakunlash"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skrinshot"</string> <string name="bugreport_title" msgid="2667494803742548533">"Xatoliklar hisoboti"</string> <string name="bugreport_message" msgid="398447048750350456">"Qurilmangiz holati haqidagi ma’lumotlar to‘planib, e-pochta orqali yuboriladi. Hisobotni tayyorlash biroz vaqt olishi mumkin."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv hisobot"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi avtomatik ravishda yoqiladi"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Saqlangan tarmoqlar ichidan signali yaxshisi hududida ekaningizda"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Qayta yoqilmasin"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"Wi‑Fi avtomatik ravishda yoqildi"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Saqlangan tarmoq atrofidasiz: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Wi-Fi tarmoqqa kirish"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Tarmoqqa kirish"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1808,6 +1811,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ilovada zaxiralash va tiklash ishlamagani uchun yorliq tiklanmadi"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ilova imzosi mos kelmagani uchun yorliq tiklanmadi"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Yorliq tiklanmadi"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Yorliq faolsizlantirildi"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"O‘chirib tashlash"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Baribir ishga tushirilsin"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Zararli ilova o‘chirilsinmi?"</string> </resources> diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml index f6752c2942fe..a7736e7c3e82 100644 --- a/core/res/res/values-watch/themes_device_defaults.xml +++ b/core/res/res/values-watch/themes_device_defaults.xml @@ -10,7 +10,7 @@ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and + See the License for the specific language gning permissions and limitations under the License. --> @@ -147,6 +147,7 @@ a similar way. an activity that looks like a Dialog.--> <style name="Theme.DeviceDefault.Dialog" parent="Theme.Material.Dialog" > <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item> <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item> @@ -183,6 +184,7 @@ a similar way. <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Dialog.Alert"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -200,6 +202,7 @@ a similar way. <style name="Theme.DeviceDefault.Settings.CompactMenu" parent="Theme.Material.CompactMenu"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -219,6 +222,7 @@ a similar way. regular dialog. --> <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -237,6 +241,7 @@ a similar way. <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar --> <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -256,6 +261,7 @@ a similar way. for a regular dialog. --> <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dark --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> @@ -434,6 +440,7 @@ a similar way. <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> <!-- Color palette Dialog --> <item name="colorPrimary">@color/primary_device_default_dark</item> <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> diff --git a/core/res/res/values-watch/themes_material.xml b/core/res/res/values-watch/themes_material.xml index 0cf398b11085..40a249ab5ed9 100644 --- a/core/res/res/values-watch/themes_material.xml +++ b/core/res/res/values-watch/themes_material.xml @@ -43,6 +43,7 @@ please see styles_device_defaults.xml. <!-- Override behaviour to set the theme colours for dialogs, keep them the same. --> <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog"> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> </style> <!-- Force the background and floating colours to be the default colours. --> @@ -51,6 +52,7 @@ please see styles_device_defaults.xml. <item name="colorBackgroundFloating">@color/background_floating_material_dark</item> <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> </style> <!-- Force the background and floating colours to be the default colours. --> @@ -59,6 +61,7 @@ please see styles_device_defaults.xml. <item name="colorBackgroundFloating">@color/background_floating_material_light</item> <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item> <item name="windowIsFloating">false</item> + <item name="windowElevation">0dp</item> </style> <!-- Force all settings themes to use normal Material theme. --> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index e91e382a85d9..e97f89392154 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -217,6 +217,7 @@ <string name="global_action_emergency" msgid="7112311161137421166">"Isimo esiphuthumayo"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Umbiko wephutha"</string> <string name="global_action_logout" msgid="935179188218826050">"Phothula isikhathi"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Isithombe-skrini"</string> <string name="bugreport_title" msgid="2667494803742548533">"Thatha umbiko wesiphazamiso"</string> <string name="bugreport_message" msgid="398447048750350456">"Lokhu kuzoqoqa ulwazi mayelana nesimo samanje sedivayisi yakho, ukuthumela imilayezo ye-imeyili. Kuzothatha isikhathi esincane kusuka ekuqaleni umbiko wesiphazamiso uze ulungele ukuthunyelwa; sicela ubekezele."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Umbiko obandakanyayo"</string> @@ -1123,6 +1124,8 @@ <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"I-Wi-Fi izovuleka ngokuzenzakalela"</string> <string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Uma useduze kwenethiwekhi yekhwalithi ephezulu elondoloziwe"</string> <string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ungaphindi uvule"</string> + <string name="wifi_wakeup_enabled_title" msgid="6534603733173085309">"I-Wi‑Fi ivuleke ngokuzenzakalela"</string> + <string name="wifi_wakeup_enabled_content" msgid="189330154407990583">"Useduzane nenethiwekhi elondoloziwe: <xliff:g id="NETWORK_SSID">%1$s</xliff:g>"</string> <string name="wifi_available_sign_in" msgid="9157196203958866662">"Ngena ngemvume kunethiwekhi ye-Wi-Fi"</string> <string name="network_available_sign_in" msgid="1848877297365446605">"Ngena ngemvume kunethiwekhi"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> @@ -1807,6 +1810,8 @@ <string name="shortcut_restore_not_supported" msgid="5028808567940014190">"Ayikwazanga ukubuyisa isinqamuleli ngoba uhlelo lokusebenza alusekeli isipele nokubuyisa"</string> <string name="shortcut_restore_signature_mismatch" msgid="2406209324521327518">"Ayikwazanga ukubuyisa isinqamuleli ngoba isignisha yohlelo lokusebenza ayifani"</string> <string name="shortcut_restore_unknown_issue" msgid="8703738064603262597">"Ayikwazanga ukubuyisa isinqamuleli"</string> - <!-- no translation found for shortcut_disabled_reason_unknown (5276016910284687075) --> - <skip /> + <string name="shortcut_disabled_reason_unknown" msgid="5276016910284687075">"Isinqamuleli sikhutshaziwe"</string> + <string name="harmful_app_warning_uninstall" msgid="3846265696369136266">"Khipha"</string> + <string name="harmful_app_warning_launch_anyway" msgid="5784428382367400530">"Qalisa noma kunjalo"</string> + <string name="harmful_app_warning_title" msgid="2229996292333310435">"Khipha uhlelo lokusebenza oluyingozi?"</string> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index addbbf5c6ce9..4eaf93d822b2 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4739,6 +4739,15 @@ <!-- Extra spacing between lines of text, as a multiplier. The value will not be applied for the last line of text.--> <attr name="lineSpacingMultiplier" format="float" /> + <!-- Explicit height between lines of text. If set, this will override the values set + for lineSpacingExtra and lineSpacingMultiplier. --> + <attr name="lineHeight" format="dimension" /> + <!-- Distance from the top of the TextView to the first text baseline. If set, this + overrides the value set for paddingTop. --> + <attr name="firstBaselineToTopHeight" format="dimension" /> + <!-- Distance from the bottom of the TextView to the last text baseline. If set, this + overrides the value set for paddingBottom. --> + <attr name="lastBaselineToBottomHeight" format="dimension" /> <!-- The number of times to repeat the marquee animation. Only applied if the TextView has marquee enabled. --> <attr name="marqueeRepeatLimit" format="integer"> @@ -4867,6 +4876,8 @@ <!-- Justification by stretching word spacing. --> <enum name="inter_word" value = "1" /> </attr> + <!-- Whether or not this view is a heading for accessibility purposes. --> + <attr name="accessibilityHeading" format="boolean"/> </declare-styleable> <declare-styleable name="TextViewAppearance"> <!-- Base text color, typeface, size, and style. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3b02a967a4b2..64f291cc92d3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -430,7 +430,7 @@ <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer> <!-- Activity name to enable wifi tethering after provisioning app succeeds --> - <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.TetherService</string> + <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string> <!-- Controls the WiFi wakeup feature. 0 = Not available. @@ -640,6 +640,12 @@ <!-- Wifi driver supports batched scan --> <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool> + <!-- Wifi driver supports Automatic channel selection (ACS) for softap --> + <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool> + + <!-- Wifi driver supports IEEE80211AC for softap --> + <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool> + <!-- Idle Receive current for wifi radio. 0 by default--> <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer> @@ -2780,8 +2786,22 @@ <!-- The bounding path of the cutout region of the main built-in display. Must either be empty if there is no cutout region, or a string that is parsable by {@link android.util.PathParser}. + The path is assumed to be specified in display coordinates with pixel units and in - the display's native orientation. --> + the display's native orientation, with the origin of the coordinate system at the + center top of the display. + + To facilitate writing device-independent emulation overlays, the marker `@dp` can be + appended after the path string to interpret coordinates in dp instead of px units. + Note that a physical cutout should be configured in pixels for the best results. + + Example for a 10px x 10px square top-center cutout: + <string ...>M -5,0 L -5,10 L 5,10 L 5,0 Z</string> + Example for a 10dp x 10dp square top-center cutout: + <string ...>M -5,0 L -5,10 L 5,10 L 5,0 Z @dp</string> + + @see https://www.w3.org/TR/SVG/paths.html#PathData + --> <string translatable="false" name="config_mainBuiltInDisplayCutout"></string> <!-- Whether the display cutout region of the main built-in display should be forced to @@ -3252,4 +3272,6 @@ <!-- Package name that should be granted Notification Assistant access --> <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string> + + <bool name="config_supportBluetoothPersistedState">true</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 58ae76cdf160..9cdf5531225b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2862,6 +2862,10 @@ <public name="appComponentFactory" /> <public name="fallbackLineSpacing" /> <public name="accessibilityPaneTitle" /> + <public name="firstBaselineToTopHeight" /> + <public name="lastBaselineToBottomHeight" /> + <public name="lineHeight" /> + <public name="accessibilityHeading" /> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b2fa294f77be..170ba4264e04 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -379,6 +379,9 @@ <string name="factory_reset_message">The admin app can\'t be used. Your device will now be erased.\n\nIf you have questions, contact your organization's admin.</string> + <!-- A toast message displayed when printing is attempted but disabled by policy. --> + <string name="printing_disabled_by">Printing disabled by <xliff:g id="owner_app">%s</xliff:g>.</string> + <!-- Display name for any time a piece of data refers to the owner of the phone. For example, this could be used in place of the phone's phone number. --> <string name="me">Me</string> @@ -3754,6 +3757,11 @@ <!-- Notification body when background data usage is limited. --> <string name="data_usage_restricted_body">Tap to remove restriction.</string> + <!-- Notification title when there has been recent excessive data usage. [CHAR LIMIT=32] --> + <string name="data_usage_rapid_title">Large data usage</string> + <!-- Notification body when there has been recent excessive data usage. [CHAR LIMIT=128] --> + <string name="data_usage_rapid_body">Your data usage over the last few days is larger than normal. Tap to view usage and settings.</string> + <!-- SSL Certificate dialogs --> <!-- Title for an SSL Certificate dialog --> <string name="ssl_certificate">Security certificate</string> @@ -4809,4 +4817,11 @@ <string name="harmful_app_warning_launch_anyway">Launch anyway</string> <!-- Title for the harmful app warning dialog. --> <string name="harmful_app_warning_title">Uninstall harmful app?</string> + + <!-- Text describing a permission request for one app to show another app's + slices [CHAR LIMIT=NONE] --> + <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string> + + <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) --> + <string name="screenshot_edit">Edit</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 638f1b25b4a5..955065706006 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -309,6 +309,8 @@ <java-symbol type="bool" name="config_useFixedVolume" /> <java-symbol type="bool" name="config_forceDefaultOrientation" /> <java-symbol type="bool" name="config_wifi_batched_scan_supported" /> + <java-symbol type="bool" name="config_wifi_softap_acs_supported" /> + <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" /> <java-symbol type="bool" name="config_enableMultiUserUI"/> <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/> <java-symbol type="bool" name="config_hasRecents" /> @@ -878,6 +880,7 @@ <java-symbol type="string" name="preposition_for_time" /> <java-symbol type="string" name="print_service_installed_title" /> <java-symbol type="string" name="print_service_installed_message" /> + <java-symbol type="string" name="printing_disabled_by" /> <java-symbol type="string" name="progress_erasing" /> <java-symbol type="string" name="mobile_provisioning_apn" /> <java-symbol type="string" name="mobile_provisioning_url" /> @@ -1507,6 +1510,7 @@ <java-symbol type="xml" name="password_kbd_symbols" /> <java-symbol type="xml" name="password_kbd_symbols_shift" /> <java-symbol type="xml" name="power_profile" /> + <java-symbol type="xml" name="power_profile_test" /> <java-symbol type="xml" name="sms_short_codes" /> <java-symbol type="xml" name="audio_assets" /> <java-symbol type="xml" name="global_keys" /> @@ -1973,6 +1977,8 @@ <java-symbol type="string" name="data_usage_warning_title" /> <java-symbol type="string" name="data_usage_wifi_limit_snoozed_title" /> <java-symbol type="string" name="data_usage_wifi_limit_title" /> + <java-symbol type="string" name="data_usage_rapid_title" /> + <java-symbol type="string" name="data_usage_rapid_body" /> <java-symbol type="string" name="default_wallpaper_component" /> <java-symbol type="string" name="device_storage_monitor_notification_channel" /> <java-symbol type="string" name="dlg_ok" /> @@ -3204,6 +3210,7 @@ <java-symbol type="string" name="global_action_logout" /> <java-symbol type="string" name="config_mainBuiltInDisplayCutout" /> + <java-symbol type="drawable" name="messaging_user" /> <java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" /> <java-symbol type="drawable" name="ic_logout" /> @@ -3218,4 +3225,10 @@ <java-symbol type="string" name="harmful_app_warning_title" /> <java-symbol type="string" name="config_defaultAssistantAccessPackage" /> + + <java-symbol type="bool" name="config_supportBluetoothPersistedState" /> + + <java-symbol type="string" name="slices_permission_request" /> + + <java-symbol type="string" name="screenshot_edit" /> </resources> diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml new file mode 100644 index 000000000000..cdb71343e5e5 --- /dev/null +++ b/core/res/res/xml/power_profile_test.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> +<device name="Android"> + <!-- All values are in mAh except as noted. + This file is for PowerProfileTest.java. Changes must be synced between these two. Since + power_profile.xml may be overridden by actual device's power_profile.xml at compile time, + this test config ensures we have something constant to test against. Values below are + sample values, not meant to reflect any real device. + --> + + <!-- Nothing --> + <item name="none">0</item> + + <!-- This is the battery capacity in mAh --> + <item name="battery.capacity">3000</item> + + <!-- Number of cores each CPU cluster contains --> + <array name="cpu.clusters.cores"> + <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) --> + <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) --> + </array> + + <!-- Power consumption when CPU is suspended --> + <item name="cpu.suspend">5</item> + <!-- Additional power consumption when CPU is in a kernel idle loop --> + <item name="cpu.idle">1.11</item> + <!-- Additional power consumption by CPU excluding cluster and core when running --> + <item name="cpu.active">2.55</item> + + <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it --> + <item name="cpu.cluster_power.cluster0">2.11</item> + <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it --> + <item name="cpu.cluster_power.cluster1">2.22</item> + + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster0"> + <value>300000</value> <!-- 300 MHz CPU speed --> + <value>1000000</value> <!-- 1000 MHz CPU speed --> + <value>2000000</value> <!-- 2000 MHz CPU speed --> + </array> + <!-- Different CPU speeds as reported in + /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies --> + <array name="cpu.core_speeds.cluster1"> + <value>300000</value> <!-- 300 MHz CPU speed --> + <value>1000000</value> <!-- 1000 MHz CPU speed --> + <value>2500000</value> <!-- 2500 MHz CPU speed --> + <value>3000000</value> <!-- 3000 MHz CPU speed --> + </array> + + <!-- Additional power used by a CPU from cluster 0 when running at different + speeds. Currently this measurement also includes cluster cost. --> + <array name="cpu.core_power.cluster0"> + <value>10</value> <!-- 300 MHz CPU speed --> + <value>20</value> <!-- 1000 MHz CPU speed --> + <value>30</value> <!-- 1900 MHz CPU speed --> + </array> + <!-- Additional power used by a CPU from cluster 1 when running at different + speeds. Currently this measurement also includes cluster cost. --> + <array name="cpu.core_power.cluster1"> + <value>25</value> <!-- 300 MHz CPU speed --> + <value>35</value> <!-- 1000 MHz CPU speed --> + <value>50</value> <!-- 2500 MHz CPU speed --> + <value>60</value> <!-- 3000 MHz CPU speed --> + </array> + + <!-- Additional power used when screen is turned on at minimum brightness --> + <item name="screen.on">100</item> + <!-- Additional power used when screen is at maximum brightness, compared to + screen at minimum brightness --> + <item name="screen.full">800</item> + + <!-- Average power used by the camera flash module when on --> + <item name="camera.flashlight">500</item> + <!-- Average power use by the camera subsystem for a typical camera + application. Intended as a rough estimate for an application running a + preview and capturing approximately 10 full-resolution pictures per + minute. --> + <item name="camera.avg">600</item> + + <!-- Additional power used when audio decoding/encoding via DSP --> + <item name="dsp.audio">100</item> + + <!-- Additional power used when GPS is acquiring a signal --> + <item name="gps.on">10</item> + + <!-- Additional power used when cellular radio is transmitting/receiving --> + <item name="radio.active">60</item> + <!-- Additional power used when cellular radio is paging the tower --> + <item name="radio.scanning">3</item> + <!-- Additional power used when the cellular radio is on. Multi-value entry, + one per signal strength (no signal, weak, moderate, strong) --> + <array name="radio.on"> <!-- Strength 0 to BINS-1 --> + <value>6</value> <!-- none --> + <value>5</value> <!-- poor --> + <value>4</value> <!-- moderate --> + <value>3</value> <!-- good --> + <value>3</value> <!-- great --> + </array> +</device> diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk new file mode 100644 index 000000000000..5b60e3167fc7 --- /dev/null +++ b/core/tests/coretests/apks/install-split-base/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := install_split_base + +include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/install-split-base/AndroidManifest.xml b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml new file mode 100644 index 000000000000..c2bfeddf32e3 --- /dev/null +++ b/core/tests/coretests/apks/install-split-base/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.install_split" + android:isolatedSplits="true"> + + <application android:label="ClassloaderSplitApp"> + <activity android:name=".BaseActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java new file mode 100644 index 000000000000..cb5760ceef8e --- /dev/null +++ b/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java @@ -0,0 +1,23 @@ +/** + * 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 com.google.android.dexapis.splitapp; + +import android.app.Activity; + +/** Main activity */ +public class BaseActivity extends Activity { +} diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk new file mode 100644 index 000000000000..0f37d16a7688 --- /dev/null +++ b/core/tests/coretests/apks/install-split-feature-a/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := install_split_feature_a + +LOCAL_USE_AAPT2 := true +LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a +LOCAL_AAPT_FLAGS += --package-id 0x80 + +include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml new file mode 100644 index 000000000000..3221c75ebe0c --- /dev/null +++ b/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.install_split" + featureSplit="feature_a"> + + <application> + <activity android:name=".feature_a.FeatureAActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java new file mode 100644 index 000000000000..0af5f893164c --- /dev/null +++ b/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java @@ -0,0 +1,23 @@ +/** + * 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 com.google.android.dexapis.splitapp.feature_a; + +import android.app.Activity; + +/** Main activity */ +public class FeatureAActivity extends Activity { +} diff --git a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java index 80e4c0214ad4..0cfcd8f85784 100644 --- a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java +++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.content.pm.crossprofile; +package android.content.pm; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.nullable; @@ -42,7 +42,7 @@ import java.util.List; /** * Build/Install/Run: - * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest + * atest frameworks/base/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java */ @Presubmit @RunWith(MockitoJUnitRunner.class) @@ -118,7 +118,7 @@ public class CrossProfileAppsTest { public void getProfileSwitchingIcon_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); - mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE); + mCrossProfileApps.getProfileSwitchingIconDrawable(MANAGED_PROFILE); verify(mResources).getDrawable(R.drawable.ic_corp_badge, null); } @@ -126,13 +126,13 @@ public class CrossProfileAppsTest { public void getProfileSwitchingIcon_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); - mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); + mCrossProfileApps.getProfileSwitchingIconDrawable(PERSONAL_PROFILE); verify(mResources).getDrawable(R.drawable.ic_account_circle, null); } @Test(expected = SecurityException.class) public void getProfileSwitchingIcon_securityException() { - mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE); + mCrossProfileApps.getProfileSwitchingIconDrawable(PERSONAL_PROFILE); } private void setValidTargetProfile(UserHandle userHandle) { diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java new file mode 100644 index 000000000000..4b8442958900 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java @@ -0,0 +1,208 @@ +/** + * 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.content.pm.dex; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.Package; +import android.content.pm.PackageParser.PackageParserException; +import android.os.FileUtils; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.frameworks.coretests.R; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import libcore.io.IoUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DexMetadataHelperTest { + private static final String APK_FILE_EXTENSION = ".apk"; + private static final String DEX_METADATA_FILE_EXTENSION = ".dm"; + + private File mTmpDir = null; + + @Before + public void setUp() { + mTmpDir = IoUtils.createTemporaryDirectory("DexMetadataHelperTest"); + } + + @After + public void tearDown() { + if (mTmpDir != null) { + File[] files = mTmpDir.listFiles(); + for (File f : files) { + f.delete(); + } + } + } + + private File createDexMetadataFile(String apkFileName) throws IOException { + File dmFile = new File(mTmpDir, apkFileName.replace(APK_FILE_EXTENSION, + DEX_METADATA_FILE_EXTENSION)); + try (FileOutputStream fos = new FileOutputStream(dmFile)) { + try (ZipOutputStream zipOs = new ZipOutputStream(fos)) { + zipOs.putNextEntry(new ZipEntry("primary.prof")); + zipOs.closeEntry(); + } + } + return dmFile; + } + + private File copyApkToToTmpDir(String apkFileName, int apkResourceId) throws IOException { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + File outFile = new File(mTmpDir, apkFileName); + try (InputStream is = context.getResources().openRawResource(apkResourceId)) { + FileUtils.copyToFileOrThrow(is, outFile); + } + return outFile; + } + + @Test + public void testParsePackageWithDmFileValid() throws IOException, PackageParserException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + createDexMetadataFile("install_split_base.apk"); + Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */); + + Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg); + assertEquals(1, packageDexMetadata.size()); + String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath); + assertNotNull(baseDexMetadata); + assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath)); + } + + @Test + public void testParsePackageSplitsWithDmFileValid() + throws IOException, PackageParserException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a); + createDexMetadataFile("install_split_base.apk"); + createDexMetadataFile("install_split_feature_a.apk"); + Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */); + + Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg); + assertEquals(2, packageDexMetadata.size()); + String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath); + assertNotNull(baseDexMetadata); + assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath)); + + String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]); + assertNotNull(splitDexMetadata); + assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0])); + } + + @Test + public void testParsePackageSplitsNoBaseWithDmFileValid() + throws IOException, PackageParserException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a); + createDexMetadataFile("install_split_feature_a.apk"); + Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */); + + Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg); + assertEquals(1, packageDexMetadata.size()); + + String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]); + assertNotNull(splitDexMetadata); + assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0])); + } + + @Test + public void testParsePackageWithDmFileInvalid() throws IOException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + File invalidDmFile = new File(mTmpDir, "install_split_base.dm"); + Files.createFile(invalidDmFile.toPath()); + try { + PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */); + DexMetadataHelper.validatePackageDexMetadata(pkg); + } catch (PackageParserException e) { + assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA); + } + } + + @Test + public void testParsePackageSplitsWithDmFileInvalid() + throws IOException, PackageParserException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a); + createDexMetadataFile("install_split_base.apk"); + File invalidDmFile = new File(mTmpDir, "install_split_feature_a.dm"); + Files.createFile(invalidDmFile.toPath()); + + try { + PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */); + DexMetadataHelper.validatePackageDexMetadata(pkg); + } catch (PackageParserException e) { + assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA); + } + } + + @Test + public void testPackageWithDmFileNoMatch() throws IOException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + createDexMetadataFile("non_existent.apk"); + + try { + DexMetadataHelper.validateDexPaths(mTmpDir.list()); + fail("Should fail validation"); + } catch (IllegalStateException e) { + // expected. + } + } + + @Test + public void testPackageSplitsWithDmFileNoMatch() + throws IOException, PackageParserException { + copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base); + copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a); + createDexMetadataFile("install_split_base.apk"); + createDexMetadataFile("install_split_feature_a.mistake.apk"); + + try { + DexMetadataHelper.validateDexPaths(mTmpDir.list()); + fail("Should fail validation"); + } catch (IllegalStateException e) { + // expected. + } + } + + private static boolean isDexMetadataForApk(String dmaPath, String apkPath) { + return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals( + dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length())); + } +} diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 417faf220d39..eaabdc8b815c 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -505,4 +505,84 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } + + @Test + public void testBuildSystemFallback_ElegantFallback_customFallback_missingFile() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " <font weight='400' style='normal' fallbackFor='serif'>NoSuchFont.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_ElegantFallback_customFallback_missingFile2() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal' fallbackFor='serif'>NoSuchFont.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 2b3969fa2c60..fa0ea5c3aa85 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -204,6 +204,7 @@ public class SettingsBackupTest { Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE, Settings.Global.ENABLE_DISKSTATS_LOGGING, Settings.Global.ENABLE_EPHEMERAL_FEATURE, + Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS, Settings.Global.ENHANCED_4G_MODE_ENABLED, Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, Settings.Global.ERROR_LOGCAT_PREFIX, @@ -330,6 +331,7 @@ public class SettingsBackupTest { Settings.Global.SETUP_PREPAID_DETECTION_REDIR_HOST, Settings.Global.SETUP_PREPAID_DETECTION_TARGET_URL, Settings.Global.SHORTCUT_MANAGER_CONSTANTS, + Settings.Global.SHOW_FIRST_CRASH_DIALOG, Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, Settings.Global.SHOW_TEMPERATURE_WARNING, Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL, @@ -352,6 +354,7 @@ public class SettingsBackupTest { Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, Settings.Global.SYS_VDSO, + Settings.Global.FPS_DEVISOR, Settings.Global.TCP_DEFAULT_INIT_RWND, Settings.Global.TETHER_DUN_APN, Settings.Global.TETHER_DUN_REQUIRED, @@ -532,7 +535,9 @@ public class SettingsBackupTest { Settings.Secure.VOICE_RECOGNITION_SERVICE, Settings.Secure.INSTANT_APPS_ENABLED, Settings.Secure.BACKUP_MANAGER_CONSTANTS, - Settings.Secure.KEYGUARD_SLICE_URI); + Settings.Secure.KEYGUARD_SLICE_URI, + Settings.Secure.PARENTAL_CONTROL_ENABLED, + Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL); @Test public void systemSettingsBackedUpOrBlacklisted() { diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java new file mode 100644 index 000000000000..4c4aeaf49855 --- /dev/null +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -0,0 +1,89 @@ +/* + * 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.provider; + +import static org.junit.Assert.fail; + +import android.platform.test.annotations.Presubmit; +import android.provider.SettingsValidators.Validator; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; + +/** Tests that ensure all backed up settings have non-null validators. */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@SmallTest +public class SettingsValidatorsTest { + + @Test + public void ensureAllBackedUpSystemSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP, + Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS); + + failIfOffendersPresent(offenders, "Settings.System"); + } + + @Test + public void ensureAllBackedUpGlobalSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP, + Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS); + + failIfOffendersPresent(offenders, "Settings.Global"); + } + + @Test + public void ensureAllBackedUpSecureSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP, + Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS); + + failIfOffendersPresent(offenders, "Settings.Secure"); + } + + private void failIfOffendersPresent(String offenders, String settingsType) { + if (offenders.length() > 0) { + fail("All " + settingsType + " settings that are backed up have to have a non-null" + + " validator, but those don't: " + offenders); + } + } + + private String getOffenders(String[] settingsToBackup, Map<String, Validator> validators) { + StringBuilder offenders = new StringBuilder(); + for (String setting : settingsToBackup) { + if (validators.get(setting) == null) { + offenders.append(setting).append(" "); + } + } + return offenders.toString(); + } + + private String[] concat(String[] first, String[] second) { + if (second == null || second.length == 0) { + return first; + } + final int firstLen = first.length; + final int secondLen = second.length; + String[] both = new String[firstLen + secondLen]; + System.arraycopy(first, 0, both, 0, firstLen); + System.arraycopy(second, 0, both, firstLen, secondLen); + return both; + } +} diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java index 5d33397e13f2..f6300ee20985 100644 --- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java +++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java @@ -132,7 +132,7 @@ public class MeasuredParagraphTest { public void buildForStaticLayout() { MeasuredParagraph mt = null; - mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null); + mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, null); assertNotNull(mt); assertNotNull(mt.getChars()); assertEquals("XXX", charsToString(mt.getChars())); @@ -147,7 +147,7 @@ public class MeasuredParagraphTest { // Recycle it MeasuredParagraph mt2 = - MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt); + MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, mt); assertEquals(mt2, mt); assertNotNull(mt2.getChars()); assertEquals("VVV", charsToString(mt.getChars())); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java index b5a7bec75197..32053e30e886 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java @@ -62,9 +62,9 @@ import java.util.Arrays; * * Build: m FrameworksCoreTests * Install: adb install -r \ - * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk + * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \ - * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner * * or * @@ -73,10 +73,18 @@ import java.util.Arrays; @SmallTest @RunWith(AndroidJUnit4.class) public class BatteryStatsCpuTimesTest { - @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader; - @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader; - @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider; - @Mock PowerProfile mPowerProfile; + @Mock + KernelUidCpuTimeReader mKernelUidCpuTimeReader; + @Mock + KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader; + @Mock + KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader; + @Mock + KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader; + @Mock + BatteryStatsImpl.UserInfoProvider mUserInfoProvider; + @Mock + PowerProfile mPowerProfile; private MockClocks mClocks; private MockBatteryStatsImpl mBatteryStatsImpl; @@ -90,6 +98,8 @@ public class BatteryStatsCpuTimesTest { mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks) .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader) .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader) + .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader) + .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader) .setUserInfoProvider(mUserInfoProvider); } @@ -134,6 +144,10 @@ public class BatteryStatsCpuTimesTest { verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable(); verify(mKernelUidCpuFreqTimeReader).readDelta( any(KernelUidCpuFreqTimeReader.Callback.class)); + verify(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + verify(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader); for (int i = 0; i < numClusters; ++i) { verify(mKernelCpuSpeedReaders[i]).readDelta(); @@ -228,7 +242,7 @@ public class BatteryStatsCpuTimesTest { updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -301,7 +315,7 @@ public class BatteryStatsCpuTimesTest { when(mUserInfoProvider.exists(testUserId)).thenReturn(true); final int isolatedAppId = FIRST_ISOLATED_UID + 27; final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, isolatedAppId, FIRST_APPLICATION_UID + 33 @@ -389,7 +403,7 @@ public class BatteryStatsCpuTimesTest { final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99); when(mUserInfoProvider.exists(testUserId)).thenReturn(true); when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -431,7 +445,7 @@ public class BatteryStatsCpuTimesTest { updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -514,10 +528,10 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { - FIRST_APPLICATION_UID + 22, - FIRST_APPLICATION_UID + 27, - FIRST_APPLICATION_UID + 33 + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 }); final long[][] uidTimesMs = { {4, 10, 5, 9, 4}, @@ -589,7 +603,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -693,7 +707,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -782,7 +796,7 @@ public class BatteryStatsCpuTimesTest { } } for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) { - for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) { + for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) { assertEquals("There shouldn't be any left-overs: " + Arrays.deepToString(expectedWakeLockUidTimesUs), 0, expectedWakeLockUidTimesUs[cluster][speed]); @@ -797,7 +811,7 @@ public class BatteryStatsCpuTimesTest { final int testUserId = 11; when(mUserInfoProvider.exists(testUserId)).thenReturn(true); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -874,7 +888,7 @@ public class BatteryStatsCpuTimesTest { when(mUserInfoProvider.exists(testUserId)).thenReturn(true); final int isolatedAppId = FIRST_ISOLATED_UID + 27; final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, isolatedAppId, FIRST_APPLICATION_UID + 33 @@ -969,7 +983,7 @@ public class BatteryStatsCpuTimesTest { final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99); when(mUserInfoProvider.exists(testUserId)).thenReturn(true); when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false); - final int[] testUids = getUids(testUserId, new int[] { + final int[] testUids = getUids(testUserId, new int[]{ FIRST_APPLICATION_UID + 22, FIRST_APPLICATION_UID + 27, FIRST_APPLICATION_UID + 33 @@ -986,7 +1000,7 @@ public class BatteryStatsCpuTimesTest { callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]); } // And one for the invalid uid - callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21}); + callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21}); return null; }).when(mKernelUidCpuFreqTimeReader).readDelta( any(KernelUidCpuFreqTimeReader.Callback.class)); @@ -1009,6 +1023,136 @@ public class BatteryStatsCpuTimesTest { verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid); } + @Test + public void testReadKernelUidCpuActiveTimesLocked() { + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + + final int testUserId = 11; + when(mUserInfoProvider.exists(testUserId)).thenReturn(true); + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 + }); + final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000}; + doAnswer(invocation -> { + final KernelUidCpuActiveTimeReader.Callback callback = + (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]); + } + return null; + }).when(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i], + u.getCpuActiveTime()); + } + + // Repeat the test when the screen is off. + + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000}; + doAnswer(invocation -> { + final KernelUidCpuActiveTimeReader.Callback callback = + (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuActiveTime(testUids[i], deltasMs[i]); + } + return null; + }).when(mKernelUidCpuActiveTimeReader).readDelta( + any(KernelUidCpuActiveTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertEquals("Unexpected cpu active time for uid=" + testUids[i], + uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime()); + } + } + + @Test + public void testReadKernelUidCpuClusterTimesLocked() { + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + + final int testUserId = 11; + when(mUserInfoProvider.exists(testUserId)).thenReturn(true); + final int[] testUids = getUids(testUserId, new int[]{ + FIRST_APPLICATION_UID + 22, + FIRST_APPLICATION_UID + 27, + FIRST_APPLICATION_UID + 33 + }); + final long[][] uidTimesMs = { + {4000, 10000}, + {5000, 1000}, + {8000, 0} + }; + doAnswer(invocation -> { + final KernelUidCpuClusterTimeReader.Callback callback = + (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]); + } + return null; + }).when(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i], + u.getCpuClusterTimes()); + } + + // Repeat the test when the screen is off. + + // PRECONDITIONS + updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); + final long[][] deltasMs = { + {3000, 12000}, + {3248327490475L, 0}, + {43000, 3345000} + }; + doAnswer(invocation -> { + final KernelUidCpuClusterTimeReader.Callback callback = + (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0]; + for (int i = 0; i < testUids.length; ++i) { + callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]); + } + return null; + }).when(mKernelUidCpuClusterTimeReader).readDelta( + any(KernelUidCpuClusterTimeReader.Callback.class)); + + // RUN + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + + // VERIFY + for (int i = 0; i < testUids.length; ++i) { + final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]); + assertNotNull("No entry for uid=" + testUids[i], u); + assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]), + u.getCpuClusterTimes()); + } + } + private void updateTimeBasesLocked(boolean unplugged, int screenState, long upTime, long realTime) { // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index e8f24567599e..702f4b8c0dc5 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -39,8 +39,11 @@ import org.junit.runners.Suite; KernelMemoryBandwidthStatsTest.class, KernelSingleUidTimeReaderTest.class, KernelUidCpuFreqTimeReaderTest.class, + KernelUidCpuActiveTimeReaderTest.class, + KernelUidCpuClusterTimeReaderTest.class, KernelWakelockReaderTest.class, - LongSamplingCounterArrayTest.class + LongSamplingCounterArrayTest.class, + PowerProfileTest.class }) public class BatteryStatsTests { } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java new file mode 100644 index 000000000000..1ac82bd1dc96 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java @@ -0,0 +1,240 @@ +/* + * 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.internal.os; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Test class for {@link KernelUidCpuActiveTimeReader}. + * + * To run it: + * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest + * + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KernelUidCpuActiveTimeReaderTest { + @Mock private BufferedReader mBufferedReader; + @Mock private KernelUidCpuActiveTimeReader.Callback mCallback; + + private KernelUidCpuActiveTimeReader mReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mReader = new KernelUidCpuActiveTimeReader(); + } + + public class Temp { + + public void method() { + method1(new long[][]{{1,2,3}, {2,3,4}}); + method1(new long[][]{{2,2,3}, {2,3,4}}); + } + public int method1(long[][] array) { + return array.length * array[0].length; + } + } + + @Test + public void testReadDelta() throws Exception { + final int cores = 8; + final String info = "active: 8"; + final int[] uids = {1, 22, 333, 4444, 5555}; + + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); + } + verifyNoMoreInteractions(mCallback); + + // Verify that a second call will only return deltas. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times1 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there won't be a callback if the proc file values didn't change. + Mockito.reset(mCallback, mBufferedReader); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that calling with a null callback doesn't result in any crashes + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times1); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, null); + + // Verify that the readDelta call will only return deltas when + // the previous call had null callback. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + } + + @Test + public void testReadDelta_malformedData() throws Exception { + final int cores = 8; + final String info = "active: 8"; + final int[] uids = {1, 22, 333, 4444, 5555}; + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i])); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if subsequent call provides wrong # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] temp = increaseTime(times); + final long[][] times1 = new long[uids.length][]; + for(int i=0;i<temp.length;i++){ + times1[i] = Arrays.copyOfRange(temp[i], 0, 6); + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified if the given core count does not match + // the following # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for(int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if any value in the proc file is -ve. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + times3[uids.length - 1][cores - 1] *= -1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length - 1; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had -ve value. + Mockito.reset(mCallback, mBufferedReader); + for (int i = 0; i < cores; i++) { + times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1]))); + verifyNoMoreInteractions(mCallback); + + // Verify that there is no callback if the values in the proc file are decreased. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times4 = increaseTime(times3); + times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i = 0; i < uids.length - 1; ++i) { + verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had decreased values. + Mockito.reset(mCallback, mBufferedReader); + for (int i = 0; i < cores; i++) { + times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1]))); + verifyNoMoreInteractions(mCallback); + } + + private long[] subtract(long[] a1, long[] a2) { + long[] val = new long[a1.length]; + for (int i = 0; i < val.length; ++i) { + val[i] = a1[i] - a2[i]; + } + return val; + } + + private String[] formatTime(int[] uids, long[][] times) { + String[] lines = new String[uids.length + 1]; + for (int i=0;i<uids.length;i++){ + StringBuilder sb = new StringBuilder(); + sb.append(uids[i]).append(':'); + for(int j=0;j<times[i].length;j++){ + sb.append(' ').append(times[i][j]); + } + lines[i] = sb.toString(); + } + lines[uids.length] = null; + return lines; + } + + private long[][] increaseTime(long[][] original) { + long[][] newTime = new long[original.length][original[0].length]; + Random rand = new Random(); + for(int i = 0;i<original.length;i++){ + for(int j=0;j<original[0].length;j++){ + newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000; + } + } + return newTime; + } + + private long getTotal(long[] times) { + long sum = 0; + for(int i=0;i<times.length;i++){ + sum+=times[i] * 10 / (i+1); + } + return sum; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java new file mode 100644 index 000000000000..0d1f85277ebb --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java @@ -0,0 +1,245 @@ +/* + * 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.internal.os; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedReader; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Test class for {@link KernelUidCpuClusterTimeReader}. + * + * To run it: + * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest + * + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KernelUidCpuClusterTimeReaderTest { + @Mock private BufferedReader mBufferedReader; + @Mock private KernelUidCpuClusterTimeReader.Callback mCallback; + + private KernelUidCpuClusterTimeReader mReader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mReader = new KernelUidCpuClusterTimeReader(); + } + + @Test + public void testReadDelta() throws Exception { + final String info = "policy0: 2 policy4: 4"; + final int cores = 6; + final int[] cluster = {2, 4}; + final int[] uids = {1, 22, 333, 4444, 5555}; + + // Verify initial call + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i])); + } + + // Verify that a second call will only return deltas. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times1 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i]))); + } + + // Verify that there won't be a callback if the proc file values didn't change. + Mockito.reset(mCallback, mBufferedReader); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that calling with a null callback doesn't result in any crashes + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times1); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, null); + + // Verify that the readDelta call will only return deltas when + // the previous call had null callback. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i]))); + } + + } + + @Test + public void testReadDelta_malformedData() throws Exception { + final String info = "policy0: 2 policy4: 4"; + final int cores = 6; + final int[] cluster = {2, 4}; + final int[] uids = {1, 22, 333, 4444, 5555}; + + // Verify initial call + final long[][] times = increaseTime(new long[uids.length][cores]); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i])); + } + + // Verify that there is no callback if subsequent call provides wrong # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] temp = increaseTime(times); + final long[][] times1 = new long[uids.length][]; + for(int i=0;i<temp.length;i++){ + times1[i] = Arrays.copyOfRange(temp[i], 0, 4); + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified if the given core count does not match + // the following # of entries. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times2 = increaseTime(times); + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i]))); + } + + // Verify that there is no callback if any value in the proc file is -ve. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times3 = increaseTime(times2); + times3[uids.length - 1][cores - 1] *= -1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length-1;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had -ve value. + Mockito.reset(mCallback, mBufferedReader); + for(int i=0;i<cores;i++){ + times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1]))); + + // // Verify that there is no callback if the values in the proc file are decreased. + Mockito.reset(mCallback, mBufferedReader); + final long[][] times4 = increaseTime(times3); + times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1; + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + for (int i=0;i<uids.length-1;i++){ + verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i]))); + } + verifyNoMoreInteractions(mCallback); + + // Verify that the internal state was not modified when the proc file had decreased values. + Mockito.reset(mCallback, mBufferedReader); + for(int i=0;i<cores;i++){ + times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000; + } + when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4)); + mReader.readDeltaInternal(mBufferedReader, mCallback); + verify(mCallback, times(1)) + .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1]))); + + } + + + private long[] subtract(long[] a1, long[] a2) { + long[] val = new long[a1.length]; + for (int i = 0; i < val.length; ++i) { + val[i] = a1[i] - a2[i]; + } + return val; + } + + private String[] formatTime(int[] uids, long[][] times) { + String[] lines = new String[uids.length + 1]; + for (int i=0;i<uids.length;i++){ + StringBuilder sb = new StringBuilder(); + sb.append(uids[i]).append(':'); + for(int j=0;j<times[i].length;j++){ + sb.append(' ').append(times[i][j]); + } + lines[i] = sb.toString(); + } + lines[uids.length] = null; + return lines; + } + + private long[][] increaseTime(long[][] original) { + long[][] newTime = new long[original.length][original[0].length]; + Random rand = new Random(); + for(int i = 0;i<original.length;i++){ + for(int j=0;j<original[0].length;j++){ + newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000; + } + } + return newTime; + } + + // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader + private long[] getTotal(int[] cluster, long[] times) { + int core = 0; + long[] sum = new long[cluster.length]; + for(int i=0;i<cluster.length;i++){ + for(int j=0;j<cluster[i];j++){ + sum[i] += times[core++] * 10 / (j+1); + } + } + return sum; + } + + // Compare array1 against flattened 2d array array2 element by element + private boolean testEqual(long[] array1, long[][] array2) { + int k=0; + for(int i=0;i<array2.length;i++){ + for(int j=0;j<array2[i].length;j++){ + if (k >= array1.length || array1[k++]!=array2[i][j])return false; + } + } + return k == array1.length; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 6c5a2aac159b..660c744f050d 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -88,6 +88,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) { + mKernelUidCpuActiveTimeReader = reader; + return this; + } + + public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) { + mKernelUidCpuClusterTimeReader = reader; + return this; + } + public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) { mKernelUidCpuTimeReader = reader; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java new file mode 100644 index 000000000000..eb7da9ca4ffc --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -0,0 +1,58 @@ +/* + * 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.internal.os; + +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; + +import junit.framework.TestCase; + +import org.junit.Before; +import org.junit.Test; + +/* + * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml + */ +@SmallTest +public class PowerProfileTest extends TestCase { + + private PowerProfile mProfile; + + @Before + public void setUp() { + mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true); + } + + @Test + public void testPowerProfile() { + assertEquals(2, mProfile.getNumCpuClusters()); + assertEquals(4, mProfile.getNumCoresInCpuCluster(0)); + assertEquals(4, mProfile.getNumCoresInCpuCluster(1)); + assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND)); + assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)); + assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE)); + assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0)); + assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1)); + assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0)); + assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2)); + assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1)); + assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3)); + assertEquals(3000.0, mProfile.getBatteryCapacity()); + } + +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index f169f225e279..4be6408dca16 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -162,9 +162,13 @@ <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" /> <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" /> + <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" /> + <assign-permission name="android.permission.DUMP" uid="incidentd" /> + <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" /> + <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" /> <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" /> @@ -194,6 +198,10 @@ <allow-in-power-save package="com.android.cellbroadcastreceiver" /> <allow-in-power-save package="com.android.shell" /> + <!-- Whitelist system providers --> + <allow-in-power-save-except-idle package="com.android.providers.calendar" /> + <allow-in-power-save-except-idle package="com.android.providers.contacts" /> + <!-- These are the packages that are white-listed to be able to run as system user --> <system-user-whitelisted-app package="com.android.settings" /> diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..e7e72714c5bb --- /dev/null +++ b/docs/html/reference/images/text/style/backgroundcolorspan.png diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png Binary files differnew file mode 100644 index 000000000000..f60db6c55254 --- /dev/null +++ b/docs/html/reference/images/text/style/foregroundcolorspan.png diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 419e2b7e4818..4d715d1cb9ea 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -26,12 +26,15 @@ import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.content.res.Resources; +import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.NinePatchDrawable; import android.net.Uri; import android.system.ErrnoException; import android.system.Os; +import android.util.DisplayMetrics; +import android.util.TypedValue; import libcore.io.IoUtils; import dalvik.system.CloseGuard; @@ -63,6 +66,19 @@ public final class ImageDecoder implements AutoCloseable { Resources getResources() { return null; } /* @hide */ + int getDensity() { return Bitmap.DENSITY_NONE; } + + /* @hide */ + int computeDstDensity() { + Resources res = getResources(); + if (res == null) { + return Bitmap.getDefaultDensity(); + } + + return res.getDisplayMetrics().densityDpi; + } + + /* @hide */ abstract ImageDecoder createImageDecoder() throws IOException; }; @@ -169,26 +185,73 @@ public final class ImageDecoder implements AutoCloseable { return decoder; } + private static class InputStreamSource extends Source { + InputStreamSource(Resources res, InputStream is, int inputDensity) { + if (is == null) { + throw new IllegalArgumentException("The InputStream cannot be null"); + } + mResources = res; + mInputStream = is; + mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE; + } + + final Resources mResources; + InputStream mInputStream; + final int mInputDensity; + + @Override + public Resources getResources() { return mResources; } + + @Override + public int getDensity() { return mInputDensity; } + + @Override + public ImageDecoder createImageDecoder() throws IOException { + + synchronized (this) { + if (mInputStream == null) { + throw new IOException("Cannot reuse InputStreamSource"); + } + InputStream is = mInputStream; + mInputStream = null; + return createFromStream(is); + } + } + } + private static class ResourceSource extends Source { ResourceSource(Resources res, int resId) { mResources = res; mResId = resId; + mResDensity = Bitmap.DENSITY_NONE; } final Resources mResources; final int mResId; + int mResDensity; @Override public Resources getResources() { return mResources; } @Override + public int getDensity() { return mResDensity; } + + @Override public ImageDecoder createImageDecoder() throws IOException { // This is just used in order to access the underlying Asset and // keep it alive. FIXME: Can we skip creating this object? InputStream is = null; ImageDecoder decoder = null; + TypedValue value = new TypedValue(); try { - is = mResources.openRawResource(mResId); + is = mResources.openRawResource(mResId, value); + + if (value.density == TypedValue.DENSITY_DEFAULT) { + mResDensity = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + mResDensity = value.density; + } + if (!(is instanceof AssetManager.AssetInputStream)) { // This should never happen. throw new RuntimeException("Resource is not an asset?"); @@ -239,11 +302,12 @@ public final class ImageDecoder implements AutoCloseable { }; /** - * Supplied to onPartialImage if the provided data is incomplete. + * Used if the provided data is incomplete. * - * Will never be thrown by ImageDecoder. + * May be thrown if there is nothing to display. * - * There may be a partial image to display. + * If supplied to onPartialImage, there may be a correct partial image to + * display. */ public static class IncompleteException extends IOException {}; @@ -293,9 +357,10 @@ public final class ImageDecoder implements AutoCloseable { }; // Fields - private long mNativePtr; - private final int mWidth; - private final int mHeight; + private long mNativePtr; + private final int mWidth; + private final int mHeight; + private final boolean mAnimated; private int mDesiredWidth; private int mDesiredHeight; @@ -321,12 +386,14 @@ public final class ImageDecoder implements AutoCloseable { * called after decoding to delete native resources. */ @SuppressWarnings("unused") - private ImageDecoder(long nativePtr, int width, int height) { + private ImageDecoder(long nativePtr, int width, int height, + boolean animated) { mNativePtr = nativePtr; mWidth = width; mHeight = height; mDesiredWidth = width; mDesiredHeight = height; + mAnimated = animated; mCloseGuard.open("close"); } @@ -416,6 +483,22 @@ public final class ImageDecoder implements AutoCloseable { } /** + * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) + * @hide + */ + public static Source createSource(Resources res, InputStream is) { + return new InputStreamSource(res, is, Bitmap.getDefaultDensity()); + } + + /** + * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable) + * @hide + */ + public static Source createSource(Resources res, InputStream is, int density) { + return new InputStreamSource(res, is, density); + } + + /** * Return the width and height of a given sample size. * * This takes an input that functions like @@ -471,6 +554,10 @@ public final class ImageDecoder implements AutoCloseable { this.resize(dimensions.x, dimensions.y); } + private boolean requestedResize() { + return mWidth != mDesiredWidth || mHeight != mDesiredHeight; + } + // These need to stay in sync with ImageDecoder.cpp's Allocator enum. /** * Use the default allocation for the pixel memory. @@ -676,6 +763,18 @@ public final class ImageDecoder implements AutoCloseable { } } + private Bitmap decodeBitmap() throws IOException { + checkState(); + // nDecodeBitmap calls postProcessAndRelease only if mPostProcess + // exists. + ImageDecoder postProcessPtr = mPostProcess == null ? null : this; + return nDecodeBitmap(mNativePtr, mOnPartialImageListener, + postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, + mMutable, mAllocator, mRequireUnpremultiplied, + mPreferRamOverQuality, mAsAlphaMask); + + } + /** * Create a {@link Drawable} from a {@code Source}. * @@ -701,8 +800,6 @@ public final class ImageDecoder implements AutoCloseable { } } - decoder.checkState(); - if (decoder.mRequireUnpremultiplied) { // Though this could be supported (ignored) for opaque images, // it seems better to always report this error. @@ -715,24 +812,35 @@ public final class ImageDecoder implements AutoCloseable { "Drawable!"); } - Bitmap bm = nDecodeBitmap(decoder.mNativePtr, - decoder.mOnPartialImageListener, - decoder.mPostProcess, - decoder.mDesiredWidth, - decoder.mDesiredHeight, - decoder.mCropRect, - false, // mMutable - decoder.mAllocator, - false, // mRequireUnpremultiplied - decoder.mPreferRamOverQuality, - decoder.mAsAlphaMask); - Resources res = src.getResources(); - if (res == null) { - bm.setDensity(Bitmap.DENSITY_NONE); + // this call potentially manipulates the decoder so it must be performed prior to + // decoding the bitmap and after decode set the density on the resulting bitmap + final int srcDensity = computeDensity(src, decoder); + if (decoder.mAnimated) { + // AnimatedImageDrawable calls postProcessAndRelease only if + // mPostProcess exists. + ImageDecoder postProcessPtr = decoder.mPostProcess == null ? + null : decoder; + Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, + postProcessPtr, decoder.mDesiredWidth, + decoder.mDesiredHeight, srcDensity, + src.computeDstDensity(), decoder.mCropRect, + decoder.mInputStream, decoder.mAssetFd); + // d has taken ownership of these objects. + decoder.mInputStream = null; + decoder.mAssetFd = null; + return d; } + Bitmap bm = decoder.decodeBitmap(); + bm.setDensity(srcDensity); + + Resources res = src.getResources(); byte[] np = bm.getNinePatchChunk(); if (np != null && NinePatch.isNinePatchChunk(np)) { + if (res != null) { + bm.setDensity(res.getDisplayMetrics().densityDpi); + } + Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); Rect padding = new Rect(); @@ -741,7 +849,6 @@ public final class ImageDecoder implements AutoCloseable { opticalInsets, null); } - // TODO: Handle animation. return new BitmapDrawable(res, bm); } } @@ -780,20 +887,46 @@ public final class ImageDecoder implements AutoCloseable { } } - decoder.checkState(); + // this call potentially manipulates the decoder so it must be performed prior to + // decoding the bitmap + final int srcDensity = computeDensity(src, decoder); + Bitmap bm = decoder.decodeBitmap(); + bm.setDensity(srcDensity); + return bm; + } + } - return nDecodeBitmap(decoder.mNativePtr, - decoder.mOnPartialImageListener, - decoder.mPostProcess, - decoder.mDesiredWidth, - decoder.mDesiredHeight, - decoder.mCropRect, - decoder.mMutable, - decoder.mAllocator, - decoder.mRequireUnpremultiplied, - decoder.mPreferRamOverQuality, - decoder.mAsAlphaMask); + // This method may modify the decoder so it must be called prior to performing the decode + private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) { + // if the caller changed the size then we treat the density as unknown + if (decoder.requestedResize()) { + return Bitmap.DENSITY_NONE; } + + // Special stuff for compatibility mode: if the target density is not + // the same as the display density, but the resource -is- the same as + // the display density, then don't scale it down to the target density. + // This allows us to load the system's density-correct resources into + // an application in compatibility mode, without scaling those down + // to the compatibility density only to have them scaled back up when + // drawn to the screen. + Resources res = src.getResources(); + final int srcDensity = src.getDensity(); + if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) { + return srcDensity; + } + + // downscale the bitmap if the asset has a higher density than the default + final int dstDensity = src.computeDstDensity(); + if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) { + float scale = (float) dstDensity / srcDensity; + int scaledWidth = (int) (decoder.mWidth * scale + 0.5f); + int scaledHeight = (int) (decoder.mHeight * scale + 0.5f); + decoder.resize(scaledWidth, scaledHeight); + return dstDensity; + } + + return srcDensity; } private String getMimeType() { @@ -808,6 +941,18 @@ public final class ImageDecoder implements AutoCloseable { return decodeBitmap(src, null); } + /** + * Private method called by JNI. + */ + @SuppressWarnings("unused") + private int postProcessAndRelease(@NonNull Canvas canvas, int width, int height) { + try { + return mPostProcess.postProcess(canvas, width, height); + } finally { + canvas.release(); + } + } + private static native ImageDecoder nCreate(long asset) throws IOException; private static native ImageDecoder nCreate(ByteBuffer buffer, int position, @@ -819,7 +964,7 @@ public final class ImageDecoder implements AutoCloseable { @NonNull private static native Bitmap nDecodeBitmap(long nativePtr, OnPartialImageListener listener, - PostProcess postProcess, + @Nullable ImageDecoder decoder, // Only used if mPostProcess != null int width, int height, Rect cropRect, boolean mutable, int allocator, boolean requireUnpremul, diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 68b7ac287e98..ef4150763139 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1025,6 +1025,10 @@ public class Typeface { xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir); if (family != null) { fallbackMap.valueAt(i).add(family); + } else if (defaultFamily != null) { + fallbackMap.valueAt(i).add(defaultFamily); + } else { + // There is no valid for for default fallback. Ignore. } } } diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java new file mode 100644 index 000000000000..da170c0fae24 --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -0,0 +1,184 @@ +/* + * 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.graphics.drawable; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.AssetFileDescriptor; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.SystemClock; +import android.util.DisplayMetrics; + +import libcore.io.IoUtils; +import libcore.util.NativeAllocationRegistry; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.Runnable; + +/** + * @hide + */ +public class AnimatedImageDrawable extends Drawable implements Animatable { + private final long mNativePtr; + private final InputStream mInputStream; + private final AssetFileDescriptor mAssetFd; + + private final int mIntrinsicWidth; + private final int mIntrinsicHeight; + + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + invalidateSelf(); + } + }; + + /** + * @hide + * This should only be called by ImageDecoder. + * + * decoder is only non-null if it has a PostProcess + */ + public AnimatedImageDrawable(long nativeImageDecoder, + @Nullable ImageDecoder decoder, int width, int height, + int srcDensity, int dstDensity, Rect cropRect, + InputStream inputStream, AssetFileDescriptor afd) + throws IOException { + width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity); + height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity); + + if (cropRect == null) { + mIntrinsicWidth = width; + mIntrinsicHeight = height; + } else { + cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity), + Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity)); + mIntrinsicWidth = cropRect.width(); + mIntrinsicHeight = cropRect.height(); + } + + mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); + mInputStream = inputStream; + mAssetFd = afd; + + // FIXME: Use the right size for the native allocation. + long nativeSize = 200; + NativeAllocationRegistry registry = new NativeAllocationRegistry( + AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); + registry.registerNativeAllocation(this, mNativePtr); + } + + @Override + protected void finalize() throws Throwable { + // FIXME: It's a shame that we have *both* a native finalizer and a Java + // one. The native one is necessary to report how much memory is being + // used natively, and this one is necessary to close the input. An + // alternative might be to read the entire stream ahead of time, so we + // can eliminate the Java finalizer. + try { + IoUtils.closeQuietly(mInputStream); + IoUtils.closeQuietly(mAssetFd); + } finally { + super.finalize(); + } + } + + @Override + public int getIntrinsicWidth() { + return mIntrinsicWidth; + } + + @Override + public int getIntrinsicHeight() { + return mIntrinsicHeight; + } + + @Override + public void draw(@NonNull Canvas canvas) { + long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper(), + SystemClock.uptimeMillis()); + scheduleSelf(mRunnable, nextUpdate); + } + + @Override + public void setAlpha(@IntRange(from=0,to=255) int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("Alpha must be between 0 and" + + " 255! provided " + alpha); + } + nSetAlpha(mNativePtr, alpha); + } + + @Override + public int getAlpha() { + return nGetAlpha(mNativePtr); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance(); + nSetColorFilter(mNativePtr, nativeFilter); + } + + @Override + public @PixelFormat.Opacity int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + // TODO: Add a Constant State? + // @Override + // public @Nullable ConstantState getConstantState() {} + + + // Animatable overrides + @Override + public boolean isRunning() { + return nIsRunning(mNativePtr); + } + + @Override + public void start() { + nStart(mNativePtr); + } + + @Override + public void stop() { + nStop(mNativePtr); + } + + private static native long nCreate(long nativeImageDecoder, + @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) + throws IOException; + private static native long nGetNativeFinalizer(); + private static native long nDraw(long nativePtr, long canvasNativePtr, long msecs); + private static native void nSetAlpha(long nativePtr, int alpha); + private static native int nGetAlpha(long nativePtr); + private static native void nSetColorFilter(long nativePtr, long nativeFilter); + private static native boolean nIsRunning(long nativePtr); + private static native void nStart(long nativePtr); + private static native void nStop(long nativePtr); + private static native long nNativeByteSize(long nativePtr); +} diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index e3740e3cf284..f74c39d84bce 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + try (FileInputStream stream = new FileInputStream(filepath)) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), + (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeFile() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), + (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeStream() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable { } } + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + Bitmap bitmap = null; try (InputStream is = r.openRawResource(srcResId, value)) { - bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); } catch (Exception e) { // Do nothing and pick up the error below. } @@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable { } } + private BitmapDrawable(BitmapState state, Resources res) { + init(state, res); + } + /** - * The one constructor to rule them all. This is called by all public + * The one helper to rule them all. This is called by all public & private * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index f17cd768c386..291b0a0be4f4 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1175,6 +1178,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily @@ -1207,6 +1214,33 @@ public abstract class Drawable { return null; } + private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + try { + ImageDecoder.Source source = null; + if (value != null) { + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + source = ImageDecoder.createSource(res, is, density); + } else { + source = ImageDecoder.createSource(res, is); + } + + return ImageDecoder.decodeDrawable(source, (info, decoder) -> { + decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR); + }); + } catch (IOException e) { + /* do nothing. + If the exception happened on decode, the drawable will be null. + */ + Log.e("Drawable", "Unable to decode stream: " + e); + } + return null; + } + /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see @@ -1306,11 +1340,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index fabcdf008c47..1690e8ce9309 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -424,15 +424,6 @@ public class KeyStore { return getmtime(key, UID_SELF); } - public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) { - try { - return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - // TODO: remove this when it's removed from Settings public boolean isHardwareBacked() { return isHardwareBacked("RSA"); diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3fb1c0d64abf..fb7b24623568 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -495,6 +495,11 @@ void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patc refPaint(paint), refBitmap(bitmap), refPatch(&patch))); } +void RecordingCanvas::drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint*) { + // Unimplemented +} + // Text void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 3087db0550de..dd06ada9da3d 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -183,6 +183,8 @@ public: virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) override; // Text virtual bool drawTextAbsolutePos() const override { return false; } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 2e08670a757a..dc274cf50a52 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -23,6 +23,7 @@ #include "hwui/MinikinUtils.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkAnimatedImage.h> #include <SkCanvasStateUtils.h> #include <SkColorFilter.h> #include <SkColorSpaceXformCanvas.h> @@ -32,6 +33,7 @@ #include <SkGraphics.h> #include <SkImage.h> #include <SkImagePriv.h> +#include <SkPicture.h> #include <SkRSXform.h> #include <SkShader.h> #include <SkTemplates.h> @@ -723,6 +725,20 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter)); } +void SkiaCanvas::drawAnimatedImage(SkAnimatedImage* image, float left, float top, + const SkPaint* paint) { + sk_sp<SkPicture> pic(image->newPictureSnapshot()); + SkMatrix matrixStorage; + SkMatrix* matrix; + if (left == 0.0f && top == 0.0f) { + matrix = nullptr; + } else { + matrixStorage = SkMatrix::MakeTrans(left, top); + matrix = &matrixStorage; + } + mCanvas->drawPicture(pic.get(), matrix, paint); +} + void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { vectorDrawable->drawStaging(this); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 99e676a6fb1e..7137210406fb 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -124,6 +124,8 @@ public: virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) override; virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index e682a2e226b7..5efd35764635 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -28,6 +28,7 @@ #include <SkCanvas.h> #include <SkMatrix.h> +class SkAnimatedImage; class SkCanvasState; class SkVertices; @@ -237,6 +238,9 @@ public: float dstTop, float dstRight, float dstBottom, const SkPaint* paint) = 0; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) = 0; + /** * Specifies if the positions passed to ::drawText are absolute or relative * to the (x,y) value provided. diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index 5f3e4071afef..b63400f14609 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -32,6 +32,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SRC_FILES := \ ../../core/java/android/os/IIncidentManager.aidl \ ../../core/java/android/os/IIncidentReportStatusListener.aidl \ + proto/android/os/header.proto \ src/IncidentReportArgs.cpp LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index 2849d580d88b..c56f689b7419 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -24,6 +24,8 @@ #include <set> #include <vector> +#include "frameworks/base/libs/incident/proto/android/os/header.pb.h" + namespace android { namespace os { @@ -47,7 +49,7 @@ public: void setAll(bool all); void setDest(int dest); void addSection(int section); - void addHeader(const vector<uint8_t>& header); + void addHeader(const IncidentHeaderProto& headerProto); inline bool all() const { return mAll; } bool containsSection(int section) const; diff --git a/core/proto/android/os/incidentheader.proto b/libs/incident/proto/android/os/header.proto index f0c736a866a6..a84dc48b8b34 100644 --- a/core/proto/android/os/incidentheader.proto +++ b/libs/incident/proto/android/os/header.proto @@ -19,31 +19,22 @@ option java_multiple_files = true; package android.os; -// IncidentHeaderProto contains extra information the caller of incidentd want to -// attach in an incident report, the data should just be informative. +// IncidentHeaderProto contains extra information the caller of incidentd wants +// to attach in an incident report, the data should just be informative. message IncidentHeaderProto { - - // From statsd config, the name of the anomaly, usually human readable. - optional string incident_name = 1; + // From statsd config, the id of the anomaly alert, unique among alerts. + optional int64 alert_id = 1; // Format a human readable reason why an incident report is requested. - // It's optional and may directly come from a user clicking the bug-report button. + // It's optional and may directly come from a user input clicking the + // bug-report button. optional string reason = 2; - // From statsd, the metric which causes the anomaly triggered. - optional string metric_name = 3; - - // From statsd, the metric value exceeds the threshold. This is useful for - // ValueMetric and GaugeMetric. - oneof metric_value { - int64 int64_value = 4; - double double_value = 5; - } - - // Defines which stats config used to fire the request. + // Defines which stats config used to fire the request, incident report will + // only be uploaded if this value is given. message StatsdConfigKey { - optional int32 uid = 1; - optional string name = 2; + optional int32 uid = 1; // The uid pushes the config to statsd. + optional int64 id = 2; // The unique id of the statsd config. } - optional StatsdConfigKey config_key = 6; + optional StatsdConfigKey config_key = 3; } diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index bd9c8eeb1d74..fbc21e558806 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -161,8 +161,14 @@ IncidentReportArgs::addSection(int section) } void -IncidentReportArgs::addHeader(const vector<uint8_t>& header) +IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto) { + vector<uint8_t> header; + auto serialized = headerProto.SerializeAsString(); + if (serialized.empty()) return; + for (auto it = serialized.begin(); it != serialized.end(); it++) { + header.push_back((uint8_t)*it); + } mHeaders.push_back(header); } diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index 833376cfa057..603926f4fe4d 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -19,10 +19,12 @@ package com.android.internal.location.gnssmetrics; import android.os.SystemClock; import android.util.Base64; +import android.util.Log; import android.util.TimeUtils; import java.util.Arrays; +import com.android.internal.app.IBatteryStats; import com.android.internal.location.nano.GnssLogsProto.GnssLog; /** @@ -31,14 +33,29 @@ import com.android.internal.location.nano.GnssLogsProto.GnssLog; */ public class GnssMetrics { + private static final String TAG = GnssMetrics.class.getSimpleName(); + + /* Constant which indicates GPS signal quality is poor */ + public static final int GPS_SIGNAL_QUALITY_POOR = 0; + + /* Constant which indicates GPS signal quality is good */ + public static final int GPS_SIGNAL_QUALITY_GOOD = 1; + + /* Number of GPS signal quality levels */ + public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1; + /** Default time between location fixes (in millisecs) */ private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000; /* The time since boot when logging started */ private String logStartInElapsedRealTime; + /* GNSS power metrics */ + private GnssPowerMetrics mGnssPowerMetrics; + /** Constructor */ - public GnssMetrics() { + public GnssMetrics(IBatteryStats stats) { + mGnssPowerMetrics = new GnssPowerMetrics(stats); locationFailureStatistics = new Statistics(); timeToFirstFixSecStatistics = new Statistics(); positionAccuracyMeterStatistics = new Statistics(); @@ -103,11 +120,18 @@ public class GnssMetrics { * */ public void logCn0(float[] cn0s, int numSv) { - if (numSv < 4) { + if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) { + if (numSv == 0) { + mGnssPowerMetrics.reportSignalQuality(null, 0); + } return; } float[] cn0Array = Arrays.copyOf(cn0s, numSv); Arrays.sort(cn0Array); + mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv); + if (numSv < 4) { + return; + } if (cn0Array[numSv - 4] > 0.0) { double top4AvgCn0 = 0.0; for (int i = numSv - 4; i < numSv; i++) { @@ -265,4 +289,62 @@ public class GnssMetrics { topFourAverageCn0Statistics.reset(); return; } + + /* Class for handling GNSS power related metrics */ + private class GnssPowerMetrics { + + /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */ + private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0; + + /* Minimum change in Top Four Average CN0 needed to trigger a report */ + private static final double REPORTING_THRESHOLD_DB_HZ = 1.0; + + /* BatteryStats API */ + private final IBatteryStats mBatteryStats; + + /* Last reported Top Four Average CN0 */ + private double mLastAverageCn0; + + public GnssPowerMetrics(IBatteryStats stats) { + mBatteryStats = stats; + // Used to initialize the variable to a very small value (unachievable in practice) so that + // the first CNO report will trigger an update to BatteryStats + mLastAverageCn0 = -100.0; + } + + /** + * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If + * the number of SVs seen is less than 4, then signal quality is the average CN0. + * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ. + */ + public void reportSignalQuality(float[] ascendingCN0Array, int numSv) { + double avgCn0 = 0.0; + if (numSv > 0) { + for (int i = Math.max(0, numSv - 4); i < numSv; i++) { + avgCn0 += (double) ascendingCN0Array[i]; + } + avgCn0 /= Math.min(numSv, 4); + } + if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) { + return; + } + try { + mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0)); + mLastAverageCn0 = avgCn0; + } catch (Exception e) { + Log.w(TAG, "Exception", e); + } + return; + } + + /** + * Obtains signal level based on CN0 + */ + private int getSignalLevel(double cn0) { + if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) { + return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD; + } + return GnssMetrics.GPS_SIGNAL_QUALITY_POOR; + } + } }
\ No newline at end of file diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index e0289f0bf336..d7861e328126 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -180,6 +180,7 @@ public final class AudioAttributes implements Parcelable { /** * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES * if applicable, as well as audioattributes.proto. + * Also consider adding them to <aaudio/AAudio.h> for the NDK. */ /** diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index 93fc3da54550..b07d04220046 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -238,22 +238,15 @@ public final class AudioFormat implements Parcelable { public static final int ENCODING_DTS = 7; /** Audio data format: DTS HD compressed */ public static final int ENCODING_DTS_HD = 8; - /** Audio data format: MP3 compressed - * @hide - * */ + /** Audio data format: MP3 compressed */ public static final int ENCODING_MP3 = 9; - /** Audio data format: AAC LC compressed - * @hide - * */ + /** Audio data format: AAC LC compressed */ public static final int ENCODING_AAC_LC = 10; - /** Audio data format: AAC HE V1 compressed - * @hide - * */ + /** Audio data format: AAC HE V1 compressed */ public static final int ENCODING_AAC_HE_V1 = 11; - /** Audio data format: AAC HE V2 compressed - * @hide - * */ + /** Audio data format: AAC HE V2 compressed */ public static final int ENCODING_AAC_HE_V2 = 12; + /** Audio data format: compressed audio wrapped in PCM for HDMI * or S/PDIF passthrough. * IEC61937 uses a stereo stream of 16-bit samples as the wrapper. @@ -266,6 +259,12 @@ public final class AudioFormat implements Parcelable { /** Audio data format: DOLBY TRUEHD compressed **/ public static final int ENCODING_DOLBY_TRUEHD = 14; + /** Audio data format: AAC ELD compressed */ + public static final int ENCODING_AAC_ELD = 15; + /** Audio data format: AAC xHE compressed */ + public static final int ENCODING_AAC_XHE = 16; + /** Audio data format: AC-4 sync frame transport format */ + public static final int ENCODING_AC4 = 17; /** @hide */ public static String toLogFriendlyEncoding(int enc) { @@ -298,6 +297,12 @@ public final class AudioFormat implements Parcelable { return "ENCODING_IEC61937"; case ENCODING_DOLBY_TRUEHD: return "ENCODING_DOLBY_TRUEHD"; + case ENCODING_AAC_ELD: + return "ENCODING_AAC_ELD"; + case ENCODING_AAC_XHE: + return "ENCODING_AAC_XHE"; + case ENCODING_AC4: + return "ENCODING_AC4"; default : return "invalid encoding " + enc; } @@ -514,6 +519,9 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_HE_V1: case ENCODING_AAC_HE_V2: case ENCODING_IEC61937: + case ENCODING_AAC_ELD: + case ENCODING_AAC_XHE: + case ENCODING_AC4: return true; default: return false; @@ -532,6 +540,13 @@ public final class AudioFormat implements Parcelable { case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_IEC61937: + case ENCODING_MP3: + case ENCODING_AAC_LC: + case ENCODING_AAC_HE_V1: + case ENCODING_AAC_HE_V2: + case ENCODING_AAC_ELD: + case ENCODING_AAC_XHE: + case ENCODING_AC4: return true; default: return false; @@ -556,6 +571,9 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_HE_V1: case ENCODING_AAC_HE_V2: case ENCODING_IEC61937: // wrapped in PCM but compressed + case ENCODING_AAC_ELD: + case ENCODING_AAC_XHE: + case ENCODING_AC4: return false; case ENCODING_INVALID: default: @@ -581,6 +599,9 @@ public final class AudioFormat implements Parcelable { case ENCODING_AAC_LC: case ENCODING_AAC_HE_V1: case ENCODING_AAC_HE_V2: + case ENCODING_AAC_ELD: + case ENCODING_AAC_XHE: + case ENCODING_AC4: return false; case ENCODING_INVALID: default: @@ -794,14 +815,7 @@ public final class AudioFormat implements Parcelable { /** * Sets the data encoding format. - * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT}, - * {@link AudioFormat#ENCODING_PCM_8BIT}, - * {@link AudioFormat#ENCODING_PCM_16BIT}, - * {@link AudioFormat#ENCODING_PCM_FLOAT}, - * {@link AudioFormat#ENCODING_AC3}, - * {@link AudioFormat#ENCODING_E_AC3}. - * {@link AudioFormat#ENCODING_DTS}, - * {@link AudioFormat#ENCODING_DTS_HD}. + * @param encoding the specified encoding or default. * @return the same Builder instance. * @throws java.lang.IllegalArgumentException */ @@ -818,6 +832,13 @@ public final class AudioFormat implements Parcelable { case ENCODING_DTS: case ENCODING_DTS_HD: case ENCODING_IEC61937: + case ENCODING_MP3: + case ENCODING_AAC_LC: + case ENCODING_AAC_HE_V1: + case ENCODING_AAC_HE_V2: + case ENCODING_AAC_ELD: + case ENCODING_AAC_XHE: + case ENCODING_AC4: mEncoding = encoding; break; case ENCODING_INVALID: @@ -1016,7 +1037,7 @@ public final class AudioFormat implements Parcelable { } /** @hide */ - @IntDef({ + @IntDef(flag = false, prefix = "ENCODING", value = { ENCODING_DEFAULT, ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, @@ -1025,8 +1046,14 @@ public final class AudioFormat implements Parcelable { ENCODING_E_AC3, ENCODING_DTS, ENCODING_DTS_HD, - ENCODING_IEC61937 - }) + ENCODING_IEC61937, + ENCODING_AAC_HE_V1, + ENCODING_AAC_HE_V2, + ENCODING_AAC_LC, + ENCODING_AAC_ELD, + ENCODING_AAC_XHE, + ENCODING_AC4 } + ) @Retention(RetentionPolicy.SOURCE) public @interface Encoding {} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 913b5e841112..2ac4063d1b08 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1329,6 +1329,19 @@ public class AudioManager { } //==================================================================== + // Offload query + /** + * Returns whether offloaded playback of an audio format is supported on the device. + * Offloaded playback is where the decoding of an audio stream is not competing with other + * software resources. In general, it is supported by dedicated hardware, such as audio DSPs. + * @param format the audio format (codec, sample rate, channels) being checked. + * @return true if the given audio format can be offloaded. + */ + public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) { + return AudioSystem.isOffloadSupported(format); + } + + //==================================================================== // Bluetooth SCO control /** * Sticky broadcast intent action indicating that the Bluetooth SCO audio @@ -3746,6 +3759,33 @@ public class AudioManager { } /** + * Indicate A2DP source or sink connection state change and eventually suppress + * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. + * @param device Bluetooth device connected/disconnected + * @param state new connection state (BluetoothProfile.STATE_xxx) + * @param profile profile for the A2DP device + * (either {@link android.bluetooth.BluetoothProfile.A2DP} or + * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) + * @param suppressNoisyIntent if true the + * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. + * @return a delay in ms that the caller should wait before broadcasting + * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent. + * {@hide} + */ + public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) { + final IAudioService service = getService(); + int delay = 0; + try { + delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, + state, profile, suppressNoisyIntent); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return delay; + } + + /** * Indicate A2DP device configuration has changed. * @param device Bluetooth device whose configuration has changed. * {@hide} diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index e56944dff782..b4316ba9b1df 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; import android.media.audiopolicy.AudioMix; @@ -818,6 +819,14 @@ public class AudioSystem public static native float getStreamVolumeDB(int stream, int index, int device); + static boolean isOffloadSupported(@NonNull AudioFormat format) { + return native_is_offload_supported(format.getEncoding(), format.getSampleRate(), + format.getChannelMask(), format.getChannelIndexMask()); + } + + private static native boolean native_is_offload_supported(int encoding, int sampleRate, + int channelMask, int channelIndexMask); + // Items shared with audio service /** @@ -914,7 +923,8 @@ public class AudioSystem (1 << STREAM_MUSIC) | (1 << STREAM_RING) | (1 << STREAM_NOTIFICATION) | - (1 << STREAM_SYSTEM); + (1 << STREAM_SYSTEM) | + (1 << STREAM_VOICE_CALL); /** * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes. diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index e535fdf53d01..5928d03dc4a1 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -24,7 +24,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.NioUtils; import java.util.Collection; +import java.util.concurrent.Executor; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -185,6 +187,22 @@ public class AudioTrack extends PlayerBase * Event id denotes when previously set update period has elapsed during playback. */ private static final int NATIVE_EVENT_NEW_POS = 4; + /** + * Callback for more data + * TODO only for offload + */ + private static final int NATIVE_EVENT_MORE_DATA = 0; + /** + * IAudioTrack tear down for offloaded tracks + * TODO: when received, java AudioTrack must be released + */ + private static final int NATIVE_EVENT_NEW_IAUDIOTRACK = 6; + /** + * Event id denotes when all the buffers queued in AF and HW are played + * back (after stop is called) for an offloaded track. + * TODO: not just for offload + */ + private static final int NATIVE_EVENT_STREAM_END = 7; private final static String TAG = "android.media.AudioTrack"; @@ -540,6 +558,12 @@ public class AudioTrack extends PlayerBase public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { + this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/); + } + + private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, + int mode, int sessionId, boolean offload) + throws IllegalArgumentException { super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK); // mState already == STATE_UNINITIALIZED @@ -601,7 +625,8 @@ public class AudioTrack extends PlayerBase // native initialization int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, - mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/); + mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, + offload); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -681,7 +706,8 @@ public class AudioTrack extends PlayerBase 0 /*mNativeBufferSizeInBytes - NA*/, 0 /*mDataLoadMode - NA*/, session, - nativeTrackInJavaObj); + nativeTrackInJavaObj, + false /*offload*/); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -729,6 +755,7 @@ public class AudioTrack extends PlayerBase * <code>MODE_STREAM</code> will be used. * <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will * be generated. + * <br>Offload is false by default. */ public static class Builder { private AudioAttributes mAttributes; @@ -737,6 +764,7 @@ public class AudioTrack extends PlayerBase private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE; private int mMode = MODE_STREAM; private int mPerformanceMode = PERFORMANCE_MODE_NONE; + private boolean mOffload = false; /** * Constructs a new Builder with the default values as described above. @@ -867,6 +895,21 @@ public class AudioTrack extends PlayerBase } /** + * Sets whether this track will play through the offloaded audio path. + * When set to true, at build time, the audio format will be checked against + * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format + * used by this track is supported on the device's offload path (if any). + * <br>Offload is only supported for media audio streams, and therefore requires that + * the usage be {@link AudioAttributes#USAGE_MEDIA}. + * @param offload true to require the offload path for playback. + * @return the same Builder instance. + */ + public @NonNull Builder setOffloadedPlayback(boolean offload) { + mOffload = offload; + return this; + } + + /** * Builds an {@link AudioTrack} instance initialized with all the parameters set * on this <code>Builder</code>. * @return a new successfully initialized {@link AudioTrack} instance. @@ -909,6 +952,19 @@ public class AudioTrack extends PlayerBase .setEncoding(AudioFormat.ENCODING_DEFAULT) .build(); } + + //TODO tie offload to PERFORMANCE_MODE_POWER_SAVING? + if (mOffload) { + if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) { + throw new UnsupportedOperationException( + "Cannot create AudioTrack, offload requires USAGE_MEDIA"); + } + if (!AudioSystem.isOffloadSupported(mFormat)) { + throw new UnsupportedOperationException( + "Cannot create AudioTrack, offload format not supported"); + } + } + try { // If the buffer size is not specified in streaming mode, // use a single frame for the buffer size and let the @@ -918,7 +974,7 @@ public class AudioTrack extends PlayerBase * mFormat.getBytesPerSample(mFormat.getEncoding()); } final AudioTrack track = new AudioTrack( - mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId); + mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload); if (track.getState() == STATE_UNINITIALIZED) { // release is not necessary throw new UnsupportedOperationException("Cannot create AudioTrack"); @@ -2882,6 +2938,69 @@ public class AudioTrack extends PlayerBase void onPeriodicNotification(AudioTrack track); } + /** + * Abstract class to receive event notification about the stream playback. + * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register + * the callback on the given {@link AudioTrack} instance. + */ + public abstract static class StreamEventCallback { + /** @hide */ // add hidden empty constructor so it doesn't show in SDK + public StreamEventCallback() { } + /** + * Called when an offloaded track is no longer valid and has been discarded by the system. + * An example of this happening is when an offloaded track has been paused too long, and + * gets invalidated by the system to prevent any other offload. + * @param track the {@link AudioTrack} on which the event happened + */ + public void onTearDown(AudioTrack track) { } + /** + * Called when all the buffers of an offloaded track that were queued in the audio system + * (e.g. the combination of the Android audio framework and the device's audio hardware) + * have been played after {@link AudioTrack#stop()} has been called. + * @param track the {@link AudioTrack} on which the event happened + */ + public void onStreamPresentationEnd(AudioTrack track) { } + /** + * Called when more audio data can be written without blocking on an offloaded track. + * @param track the {@link AudioTrack} on which the event happened + */ + public void onStreamDataRequest(AudioTrack track) { } + } + + private Executor mStreamEventExec; + private StreamEventCallback mStreamEventCb; + private final Object mStreamEventCbLock = new Object(); + + /** + * Sets the callback for the notification of stream events. + * @param executor {@link Executor} to handle the callbacks + * @param eventCallback the callback to receive the stream event notifications + */ + public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull StreamEventCallback eventCallback) { + if (eventCallback == null) { + throw new IllegalArgumentException("Illegal null StreamEventCallback"); + } + if (executor == null) { + throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback"); + } + synchronized (mStreamEventCbLock) { + mStreamEventExec = executor; + mStreamEventCb = eventCallback; + } + } + + /** + * Unregisters the callback for notification of stream events, previously set + * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}. + */ + public void removeStreamEventCallback() { + synchronized (mStreamEventCbLock) { + mStreamEventExec = null; + mStreamEventCb = null; + } + } + //--------------------------------------------------------- // Inner classes //-------------------- @@ -2965,7 +3084,7 @@ public class AudioTrack extends PlayerBase private static void postEventFromNative(Object audiotrack_ref, int what, int arg1, int arg2, Object obj) { //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2); - AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get(); + final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get(); if (track == null) { return; } @@ -2974,6 +3093,32 @@ public class AudioTrack extends PlayerBase track.broadcastRoutingChange(); return; } + + if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK + || what == NATIVE_EVENT_STREAM_END) { + final Executor exec; + final StreamEventCallback cb; + synchronized (track.mStreamEventCbLock) { + exec = track.mStreamEventExec; + cb = track.mStreamEventCb; + } + if ((exec == null) || (cb == null)) { + return; + } + switch (what) { + case NATIVE_EVENT_MORE_DATA: + exec.execute(() -> cb.onStreamDataRequest(track)); + return; + case NATIVE_EVENT_NEW_IAUDIOTRACK: + // TODO also release track as it's not longer usable + exec.execute(() -> cb.onTearDown(track)); + return; + case NATIVE_EVENT_STREAM_END: + exec.execute(() -> cb.onStreamPresentationEnd(track)); + return; + } + } + NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate; if (delegate != null) { Handler handler = delegate.getHandler(); @@ -2995,7 +3140,8 @@ public class AudioTrack extends PlayerBase private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this, Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, - int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack); + int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, + boolean offload); private native final void native_finalize(); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index bb6ae9863d31..6c6522328e8d 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -203,5 +203,8 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); + int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, + int state, int profile, boolean suppressNoisyIntent); + // WARNING: read warning at top of file, it is recommended to add new methods at the end } diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 3c49b80b4b5e..78477f757e2a 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -1380,7 +1380,8 @@ public class MediaRecorder implements AudioRouting if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( - listener, new NativeRoutingEventHandlerDelegate(this, listener, handler)); + listener, new NativeRoutingEventHandlerDelegate(this, listener, + handler != null ? handler : mEventHandler)); } } } @@ -1401,36 +1402,6 @@ public class MediaRecorder implements AudioRouting } } - /** - * Helper class to handle the forwarding of native events to the appropriate listener - * (potentially) handled in a different thread - */ - private class NativeRoutingEventHandlerDelegate { - private MediaRecorder mMediaRecorder; - private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; - private Handler mHandler; - - NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder, - final AudioRouting.OnRoutingChangedListener listener, Handler handler) { - mMediaRecorder = mediaRecorder; - mOnRoutingChangedListener = listener; - mHandler = handler != null ? handler : mEventHandler; - } - - void notifyClient() { - if (mHandler != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - if (mOnRoutingChangedListener != null) { - mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder); - } - } - }); - } - } - } - private native final boolean native_setInputDevice(int deviceId); private native final int native_getRoutedDeviceId(); private native final void native_enableDeviceCallback(boolean enabled); diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java index 07483f60c69e..b928e9319b18 100644 --- a/media/java/android/media/update/ApiLoader.java +++ b/media/java/android/media/update/ApiLoader.java @@ -16,8 +16,10 @@ package android.media.update; +import android.content.res.Resources; import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager; +import android.os.Build; /** * @hide @@ -34,23 +36,25 @@ public final class ApiLoader { public static StaticProvider getProvider(Context context) { try { return (StaticProvider) getMediaLibraryImpl(context); - } catch (NameNotFoundException | ReflectiveOperationException e) { + } catch (PackageManager.NameNotFoundException | ReflectiveOperationException e) { throw new RuntimeException(e); } } // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode. - private static synchronized Object getMediaLibraryImpl(Context appContext) - throws NameNotFoundException, ReflectiveOperationException { + private static synchronized Object getMediaLibraryImpl(Context context) + throws PackageManager.NameNotFoundException, ReflectiveOperationException { if (sMediaLibrary != null) return sMediaLibrary; - // TODO Dynamically find the package name - Context libContext = appContext.createPackageContext(UPDATE_PACKAGE, + // TODO Figure out when to use which package (query media update service) + int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_FACTORY_ONLY; + Context libContext = context.createApplicationContext( + context.getPackageManager().getPackageInfo(UPDATE_PACKAGE, flags).applicationInfo, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); sMediaLibrary = libContext.getClassLoader() .loadClass(UPDATE_CLASS) - .getMethod(UPDATE_METHOD, Context.class, Context.class) - .invoke(null, appContext, libContext); + .getMethod(UPDATE_METHOD, Resources.class, Resources.Theme.class) + .invoke(null, libContext.getResources(), libContext.getTheme()); return sMediaLibrary; } } diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java index 71fbd084e643..83763b41bcaa 100644 --- a/media/java/android/media/update/MediaController2Provider.java +++ b/media/java/android/media/update/MediaControlView2Provider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 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. @@ -19,7 +19,6 @@ package android.media.update; import android.annotation.SystemApi; import android.media.session.MediaController; import android.view.View; -import android.view.View.OnClickListener; /** * Interface for connecting the public API to an updatable implementation. @@ -30,19 +29,17 @@ import android.view.View.OnClickListener; * * All methods behave as per their namesake in the public API. * - * @see android.widget.MediaController2 + * @see android.widget.MediaControlView2 * * @hide */ // TODO @SystemApi -public interface MediaController2Provider extends ViewProvider { +public interface MediaControlView2Provider extends ViewProvider { void setController_impl(MediaController controller); - void setAnchorView_impl(View view); void show_impl(); void show_impl(int timeout); boolean isShowing_impl(); void hide_impl(); - void setPrevNextListeners_impl(OnClickListener next, OnClickListener prev); void showCCButton_impl(); boolean isPlaying_impl(); int getCurrentPosition_impl(); diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java index a1e2404ca78a..1a0df52413d2 100644 --- a/media/java/android/media/update/StaticProvider.java +++ b/media/java/android/media/update/StaticProvider.java @@ -16,8 +16,10 @@ package android.media.update; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.widget.MediaController2; +import android.util.AttributeSet; +import android.widget.MediaControlView2; import android.widget.VideoView2; /** @@ -30,7 +32,9 @@ import android.widget.VideoView2; */ // TODO @SystemApi public interface StaticProvider { - MediaController2Provider createMediaController2( - MediaController2 instance, ViewProvider superProvider); - VideoView2Provider createVideoView2(VideoView2 instance, ViewProvider superProvider); + MediaControlView2Provider createMediaControlView2( + MediaControlView2 instance, ViewProvider superProvider); + VideoView2Provider createVideoView2( + VideoView2 instance, ViewProvider superProvider, + @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes); } diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java index 6fc9bdc64e02..b7a24e59ab3e 100644 --- a/media/java/android/media/update/VideoView2Provider.java +++ b/media/java/android/media/update/VideoView2Provider.java @@ -17,9 +17,8 @@ package android.media.update; import android.media.AudioAttributes; -import android.media.MediaPlayer; import android.net.Uri; -import android.widget.MediaController2; +import android.widget.MediaControlView2; import android.widget.VideoView2; import java.util.Map; @@ -39,6 +38,8 @@ import java.util.Map; */ // TODO @SystemApi public interface VideoView2Provider extends ViewProvider { + void setMediaControlView2_impl(MediaControlView2 mediaControlView); + MediaControlView2 getMediaControlView2_impl(); void start_impl(); void pause_impl(); int getDuration_impl(); @@ -49,18 +50,19 @@ public interface VideoView2Provider extends ViewProvider { int getAudioSessionId_impl(); void showSubtitle_impl(); void hideSubtitle_impl(); + void setSpeed_impl(float speed); + float getSpeed_impl(); void setAudioFocusRequest_impl(int focusGain); void setAudioAttributes_impl(AudioAttributes attributes); void setVideoPath_impl(String path); void setVideoURI_impl(Uri uri); void setVideoURI_impl(Uri uri, Map<String, String> headers); - void setMediaController2_impl(MediaController2 controllerView); void setViewType_impl(int viewType); int getViewType_impl(); void stopPlayback_impl(); - void setOnPreparedListener_impl(MediaPlayer.OnPreparedListener l); - void setOnCompletionListener_impl(MediaPlayer.OnCompletionListener l); - void setOnErrorListener_impl(MediaPlayer.OnErrorListener l); - void setOnInfoListener_impl(MediaPlayer.OnInfoListener l); + void setOnPreparedListener_impl(VideoView2.OnPreparedListener l); + void setOnCompletionListener_impl(VideoView2.OnCompletionListener l); + void setOnErrorListener_impl(VideoView2.OnErrorListener l); + void setOnInfoListener_impl(VideoView2.OnInfoListener l); void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l); } diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java index bc8f20302d35..e54240433121 100644 --- a/media/java/android/media/update/ViewProvider.java +++ b/media/java/android/media/update/ViewProvider.java @@ -37,10 +37,6 @@ import android.view.MotionEvent; // TODO @SystemApi public interface ViewProvider { // TODO Add more (all?) methods from View - void onAttachedToWindow_impl(); - void onDetachedFromWindow_impl(); - void onLayout_impl(boolean changed, int left, int top, int right, int bottom); - void draw_impl(Canvas canvas); CharSequence getAccessibilityClassName_impl(); boolean onTouchEvent_impl(MotionEvent ev); boolean onTrackballEvent_impl(MotionEvent ev); diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 61164e068da6..0704e3545b62 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -29,6 +29,13 @@ cc_library_shared { shared_libs: [ "libandroid_runtime", ], + + arch: { + arm: { + // TODO: This is to work around b/24465209. Remove after root cause is fixed + ldflags: ["-Wl,--hash-style=both"], + }, + }, } // The headers module is in frameworks/native/Android.bp. diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml index 291009ef7005..45e557c00333 100644 --- a/packages/ExtServices/AndroidManifest.xml +++ b/packages/ExtServices/AndroidManifest.xml @@ -51,6 +51,19 @@ </intent-filter> </service> + <service android:name=".autofill.AutofillFieldClassificationServiceImpl" + android:permission="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"> + <intent-filter> + <action android:name="android.service.autofill.AutofillFieldClassificationService" /> + </intent-filter> + <meta-data + android:name="android.autofill.field_classification.default_algorithm" + android:resource="@string/autofill_field_classification_default_algorithm" /> + <meta-data + android:name="android.autofill.field_classification.available_algorithms" + android:resource="@array/autofill_field_classification_available_algorithms" /> + </service> + <library android:name="android.ext.services"/> </application> diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml index a2e65bc8f9fd..72647ab8ae3f 100644 --- a/packages/ExtServices/res/values/strings.xml +++ b/packages/ExtServices/res/values/strings.xml @@ -19,4 +19,9 @@ <string name="notification_assistant">Notification Assistant</string> <string name="prompt_block_reason">Too many dismissals:views</string> + + <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string> + <string-array name="autofill_field_classification_available_algorithms"> + <item>EDIT_DISTANCE</item> + </string-array> </resources> diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java new file mode 100644 index 000000000000..4709d35018c2 --- /dev/null +++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java @@ -0,0 +1,69 @@ +/* + * 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.ext.services.autofill; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.service.autofill.AutofillFieldClassificationService; +import android.util.Log; +import android.view.autofill.AutofillValue; + +import com.android.internal.util.ArrayUtils; + +import java.util.List; + +public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService { + + private static final String TAG = "AutofillFieldClassificationServiceImpl"; + // TODO(b/70291841): set to false before launching + private static final boolean DEBUG = true; + + @Nullable + @Override + public float[][] onGetScores(@Nullable String algorithmName, + @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues, + @NonNull List<String> userDataValues) { + if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) { + Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues (" + + userDataValues + ")"); + // TODO(b/70939974): add unit test + return null; + } + if (algorithmName != null && !algorithmName.equals(EditDistanceScorer.NAME)) { + Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " + + EditDistanceScorer.NAME + " instead"); + } + + final String actualAlgorithmName = EditDistanceScorer.NAME; + final int actualValuesSize = actualValues.size(); + final int userDataValuesSize = userDataValues.size(); + if (DEBUG) { + Log.d(TAG, "getScores() will return a " + actualValuesSize + "x" + + userDataValuesSize + " matrix for " + actualAlgorithmName); + } + final float[][] scores = new float[actualValuesSize][userDataValuesSize]; + + final EditDistanceScorer algorithm = EditDistanceScorer.getInstance(); + for (int i = 0; i < actualValuesSize; i++) { + for (int j = 0; j < userDataValuesSize; j++) { + final float score = algorithm.getScore(actualValues.get(i), userDataValues.get(j)); + scores[i][j] = score; + } + } + return scores; + } +} diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java index 97a386866665..d2e804af1b43 100644 --- a/core/java/android/service/autofill/EditDistanceScorer.java +++ b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.service.autofill; +package android.ext.services.autofill; import android.annotation.NonNull; -import android.annotation.TestApi; import android.view.autofill.AutofillValue; /** @@ -24,20 +23,15 @@ import android.view.autofill.AutofillValue; * by the user and the expected value predicted by an autofill service. */ // TODO(b/70291841): explain algorithm once it's fully implemented -/** @hide */ -@TestApi -public final class EditDistanceScorer { +final class EditDistanceScorer { private static final EditDistanceScorer sInstance = new EditDistanceScorer(); - /** @hide */ public static final String NAME = "EDIT_DISTANCE"; /** * Gets the singleton instance. */ - @TestApi - /** @hide */ public static EditDistanceScorer getInstance() { return sInstance; } @@ -52,9 +46,7 @@ public final class EditDistanceScorer { * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and * partial mathces are something in between, typically using edit-distance algorithms. * - * @hide */ - @TestApi public float getScore(@NonNull AutofillValue actualValue, @NonNull String userDataValue) { if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java new file mode 100644 index 000000000000..cc1571920e86 --- /dev/null +++ b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.ext.services.autofill; + +import static com.google.common.truth.Truth.assertThat; + +import android.support.test.runner.AndroidJUnit4; +import android.view.autofill.AutofillValue; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class EditDistanceScorerTest { + + private final EditDistanceScorer mScorer = EditDistanceScorer.getInstance(); + + @Test + public void testGetScore_nullValue() { + assertFloat(mScorer.getScore(null, "D'OH!"), 0); + } + + @Test + public void testGetScore_nonTextValue() { + assertFloat(mScorer.getScore(AutofillValue.forToggle(true), "D'OH!"), 0); + } + + @Test + public void testGetScore_nullUserData() { + assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), null), 0); + } + + @Test + public void testGetScore_fullMatch() { + assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1); + } + + @Test + public void testGetScore_fullMatchMixedCase() { + assertFloat(mScorer.getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1); + } + + // TODO(b/70291841): might need to change it once it supports different sizes + @Test + public void testGetScore_mismatchDifferentSizes() { + assertFloat(mScorer.getScore(AutofillValue.forText("One"), "MoreThanOne"), 0); + assertFloat(mScorer.getScore(AutofillValue.forText("MoreThanOne"), "One"), 0); + } + + @Test + public void testGetScore_partialMatch() { + assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F); + assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F); + assertFloat(mScorer.getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F); + assertFloat(mScorer.getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F); + assertFloat(mScorer.getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F); + assertFloat(mScorer.getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F); + } + + public static void assertFloat(float actualValue, float expectedValue) { + assertThat(actualValue).isWithin(1.0e-10f).of(expectedValue); + } +} diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml index 55c192d56a4d..e1d5c0ff01de 100644 --- a/packages/SettingsLib/res/layout/settings_with_drawer.xml +++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml @@ -13,34 +13,28 @@ See the License for the specific language governing permissions and limitations under the License. --> -<android.support.v4.widget.DrawerLayout +<!-- The main content view --> +<LinearLayout + android:id="@+id/content_parent" xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/drawer_layout" android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- The main content view --> - <LinearLayout - android:id="@+id/content_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <Toolbar + android:id="@+id/action_bar" + style="?android:attr/actionBarStyle" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:fitsSystemWindows="true"> - <Toolbar - android:id="@+id/action_bar" - style="?android:attr/actionBarStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:theme="?android:attr/actionBarTheme" - android:navigationContentDescription="@*android:string/action_bar_up_description"/> - <FrameLayout - android:id="@+id/content_header_container" - style="?android:attr/actionBarStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content"/> - <FrameLayout - android:id="@+id/content_frame" - android:layout_width="match_parent" - android:layout_height="fill_parent" - android:background="?android:attr/windowBackground" /> - </LinearLayout> -</android.support.v4.widget.DrawerLayout> + android:layout_height="wrap_content" + android:theme="?android:attr/actionBarTheme" + android:navigationContentDescription="@*android:string/action_bar_up_description" /> + <FrameLayout + android:id="@+id/content_header_container" + style="?android:attr/actionBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <FrameLayout + android:id="@+id/content_frame" + android:layout_width="match_parent" + android:layout_height="fill_parent" + android:background="?android:attr/windowBackground" /> +</LinearLayout> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b035b70c92c9..b13de2ec5ce1 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -498,10 +498,6 @@ <string name="wifi_display_certification">Wireless display certification</string> <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] --> <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string> - <!-- Setting Checkbox title whether to enable WiFi Aggressive Handover (more actively switch from wifi to mobile data). [CHAR LIMIT=40] --> - <string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to mobile handover</string> - <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] --> - <string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string> <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] --> <string name="mobile_data_always_on">Mobile data always active</string> <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] --> @@ -556,10 +552,6 @@ <string name="wifi_display_certification_summary">Show options for wireless display certification</string> <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] --> <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string> - <!-- Setting Checkbox summary whether to enable Wifi aggressive handover [CHAR LIMIT=130] --> - <string name="wifi_aggressive_handover_summary">When enabled, Wi\u2011Fi will be more aggressive in handing over the data connection to mobile, when Wi\u2011Fi signal is low</string> - <!-- Setting Checkbox summary whether to always allow WiFi Roam Scans [CHAR LIMIT=130] --> - <string name="wifi_allow_scan_with_traffic_summary">Allow/Disallow Wi\u2011Fi Roam Scans based on the amount of data traffic present at the interface</string> <!-- UI debug setting: limit size of Android logger buffers --> <string name="select_logd_size_title">Logger buffer sizes</string> <!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java new file mode 100644 index 000000000000..72273046ef29 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java @@ -0,0 +1,110 @@ +/* + * 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. + */ + +package com.android.settingslib.core.instrumentation; + +import android.content.Context; +import android.metrics.LogMaker; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; + +/** + * {@link LogWriter} that writes data to eventlog. + */ +public class EventLogWriter implements LogWriter { + + private final MetricsLogger mMetricsLogger = new MetricsLogger(); + + public void visible(Context context, int source, int category) { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_OPEN) + .addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); + MetricsLogger.action(logMaker); + } + + public void hidden(Context context, int category) { + MetricsLogger.hidden(context, category); + } + + public void action(int category, int value, Pair<Integer, Object>... taggedData) { + if (taggedData == null || taggedData.length == 0) { + mMetricsLogger.action(category, value); + } else { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setSubtype(value); + for (Pair<Integer, Object> pair : taggedData) { + logMaker.addTaggedData(pair.first, pair.second); + } + mMetricsLogger.write(logMaker); + } + } + + public void action(int category, boolean value, Pair<Integer, Object>... taggedData) { + action(category, value ? 1 : 0, taggedData); + } + + public void action(Context context, int category, Pair<Integer, Object>... taggedData) { + action(context, category, "", taggedData); + } + + public void actionWithSource(Context context, int source, int category) { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION); + if (source != MetricsProto.MetricsEvent.VIEW_UNKNOWN) { + logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, source); + } + MetricsLogger.action(logMaker); + } + + /** @deprecated use {@link #action(int, int, Pair[])} */ + @Deprecated + public void action(Context context, int category, int value) { + MetricsLogger.action(context, category, value); + } + + /** @deprecated use {@link #action(int, boolean, Pair[])} */ + @Deprecated + public void action(Context context, int category, boolean value) { + MetricsLogger.action(context, category, value); + } + + public void action(Context context, int category, String pkg, + Pair<Integer, Object>... taggedData) { + if (taggedData == null || taggedData.length == 0) { + MetricsLogger.action(context, category, pkg); + } else { + final LogMaker logMaker = new LogMaker(category) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setPackageName(pkg); + for (Pair<Integer, Object> pair : taggedData) { + logMaker.addTaggedData(pair.first, pair.second); + } + MetricsLogger.action(logMaker); + } + } + + public void count(Context context, String name, int value) { + MetricsLogger.count(context, name, value); + } + + public void histogram(Context context, String name, int bucket) { + MetricsLogger.histogram(context, name, bucket); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java new file mode 100644 index 000000000000..dbc61c26e82e --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/Instrumentable.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package com.android.settingslib.core.instrumentation; + +public interface Instrumentable { + + int METRICS_CATEGORY_UNKNOWN = 0; + + /** + * Instrumented name for a view as defined in + * {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}. + */ + int getMetricsCategory(); +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java new file mode 100644 index 000000000000..4b9f5727208d --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java @@ -0,0 +1,84 @@ +/* + * 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. + */ +package com.android.settingslib.core.instrumentation; + +import android.content.Context; +import android.util.Pair; + +/** + * Generic log writer interface. + */ +public interface LogWriter { + + /** + * Logs a visibility event when view becomes visible. + */ + void visible(Context context, int source, int category); + + /** + * Logs a visibility event when view becomes hidden. + */ + void hidden(Context context, int category); + + /** + * Logs a user action. + */ + void action(int category, int value, Pair<Integer, Object>... taggedData); + + /** + * Logs a user action. + */ + void action(int category, boolean value, Pair<Integer, Object>... taggedData); + + /** + * Logs an user action. + */ + void action(Context context, int category, Pair<Integer, Object>... taggedData); + + /** + * Logs an user action. + */ + void actionWithSource(Context context, int source, int category); + + /** + * Logs an user action. + * @deprecated use {@link #action(int, int, Pair[])} + */ + @Deprecated + void action(Context context, int category, int value); + + /** + * Logs an user action. + * @deprecated use {@link #action(int, boolean, Pair[])} + */ + @Deprecated + void action(Context context, int category, boolean value); + + /** + * Logs an user action. + */ + void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData); + + /** + * Logs a count. + */ + void count(Context context, String name, int value); + + /** + * Logs a histogram event. + */ + void histogram(Context context, String name, int bucket); +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java new file mode 100644 index 000000000000..1e5b378e931c --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java @@ -0,0 +1,159 @@ +/* + * 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. + */ +package com.android.settingslib.core.instrumentation; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import java.util.ArrayList; +import java.util.List; + +/** + * FeatureProvider for metrics. + */ +public class MetricsFeatureProvider { + private List<LogWriter> mLoggerWriters; + + public MetricsFeatureProvider() { + mLoggerWriters = new ArrayList<>(); + installLogWriters(); + } + + protected void installLogWriters() { + mLoggerWriters.add(new EventLogWriter()); + } + + public void visible(Context context, int source, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.visible(context, source, category); + } + } + + public void hidden(Context context, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.hidden(context, category); + } + } + + public void actionWithSource(Context context, int source, int category) { + for (LogWriter writer : mLoggerWriters) { + writer.actionWithSource(context, source, category); + } + } + + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + + /** + * Logs a user action. Includes the elapsed time since the containing + * fragment has been visible. + */ + public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(category, value, + sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible())); + } + } + + public void action(Context context, int category, Pair<Integer, Object>... taggedData) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, taggedData); + } + } + + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */ + @Deprecated + public void action(Context context, int category, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, value); + } + } + + /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */ + @Deprecated + public void action(Context context, int category, boolean value) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, value); + } + } + + public void action(Context context, int category, String pkg, + Pair<Integer, Object>... taggedData) { + for (LogWriter writer : mLoggerWriters) { + writer.action(context, category, pkg, taggedData); + } + } + + public void count(Context context, String name, int value) { + for (LogWriter writer : mLoggerWriters) { + writer.count(context, name, value); + } + } + + public void histogram(Context context, String name, int bucket) { + for (LogWriter writer : mLoggerWriters) { + writer.histogram(context, name, bucket); + } + } + + public int getMetricsCategory(Object object) { + if (object == null || !(object instanceof Instrumentable)) { + return MetricsEvent.VIEW_UNKNOWN; + } + return ((Instrumentable) object).getMetricsCategory(); + } + + public void logDashboardStartIntent(Context context, Intent intent, + int sourceMetricsCategory) { + if (intent == null) { + return; + } + final ComponentName cn = intent.getComponent(); + if (cn == null) { + final String action = intent.getAction(); + if (TextUtils.isEmpty(action)) { + // Not loggable + return; + } + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action, + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + return; + } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) { + // Going to a Setting internal page, skip click logging in favor of page's own + // visibility logging. + return; + } + action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(), + Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory)); + } + + private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) { + return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java new file mode 100644 index 000000000000..facce4e0bcbb --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java @@ -0,0 +1,259 @@ +/* + * 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. + */ + +package com.android.settingslib.core.instrumentation; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +public class SharedPreferencesLogger implements SharedPreferences { + + private static final String LOG_TAG = "SharedPreferencesLogger"; + + private final String mTag; + private final Context mContext; + private final MetricsFeatureProvider mMetricsFeature; + private final Set<String> mPreferenceKeySet; + + public SharedPreferencesLogger(Context context, String tag, + MetricsFeatureProvider metricsFeature) { + mContext = context; + mTag = tag; + mMetricsFeature = metricsFeature; + mPreferenceKeySet = new ConcurrentSkipListSet<>(); + } + + @Override + public Map<String, ?> getAll() { + return null; + } + + @Override + public String getString(String key, @Nullable String defValue) { + return defValue; + } + + @Override + public Set<String> getStringSet(String key, @Nullable Set<String> defValues) { + return defValues; + } + + @Override + public int getInt(String key, int defValue) { + return defValue; + } + + @Override + public long getLong(String key, long defValue) { + return defValue; + } + + @Override + public float getFloat(String key, float defValue) { + return defValue; + } + + @Override + public boolean getBoolean(String key, boolean defValue) { + return defValue; + } + + @Override + public boolean contains(String key) { + return false; + } + + @Override + public Editor edit() { + return new EditorLogger(); + } + + @Override + public void registerOnSharedPreferenceChangeListener( + OnSharedPreferenceChangeListener listener) { + } + + @Override + public void unregisterOnSharedPreferenceChangeListener( + OnSharedPreferenceChangeListener listener) { + } + + private void logValue(String key, Object value) { + logValue(key, value, false /* forceLog */); + } + + private void logValue(String key, Object value, boolean forceLog) { + final String prefKey = buildPrefKey(mTag, key); + if (!forceLog && !mPreferenceKeySet.contains(prefKey)) { + // Pref key doesn't exist in set, this is initial display so we skip metrics but + // keeps track of this key. + mPreferenceKeySet.add(prefKey); + return; + } + // TODO: Remove count logging to save some resource. + mMetricsFeature.count(mContext, buildCountName(prefKey, value), 1); + + final Pair<Integer, Object> valueData; + if (value instanceof Long) { + final Long longVal = (Long) value; + final int intVal; + if (longVal > Integer.MAX_VALUE) { + intVal = Integer.MAX_VALUE; + } else if (longVal < Integer.MIN_VALUE) { + intVal = Integer.MIN_VALUE; + } else { + intVal = longVal.intValue(); + } + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + intVal); + } else if (value instanceof Integer) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + value); + } else if (value instanceof Boolean) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, + (Boolean) value ? 1 : 0); + } else if (value instanceof Float) { + valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, + value); + } else if (value instanceof String) { + Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value); + valueData = null; + } else { + Log.w(LOG_TAG, "Tried to log unloggable object" + value); + valueData = null; + } + if (valueData != null) { + // Pref key exists in set, log it's change in metrics. + mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, + Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey), + valueData); + } + } + + @VisibleForTesting + void logPackageName(String key, String value) { + final String prefKey = mTag + "/" + key; + mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value, + Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey)); + } + + private void safeLogValue(String key, String value) { + new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value); + } + + public static String buildCountName(String prefKey, Object value) { + return prefKey + "|" + value; + } + + public static String buildPrefKey(String tag, String key) { + return tag + "/" + key; + } + + private class AsyncPackageCheck extends AsyncTask<String, Void, Void> { + @Override + protected Void doInBackground(String... params) { + String key = params[0]; + String value = params[1]; + PackageManager pm = mContext.getPackageManager(); + try { + // Check if this might be a component. + ComponentName name = ComponentName.unflattenFromString(value); + if (value != null) { + value = name.getPackageName(); + } + } catch (Exception e) { + } + try { + pm.getPackageInfo(value, PackageManager.MATCH_ANY_USER); + logPackageName(key, value); + } catch (PackageManager.NameNotFoundException e) { + // Clearly not a package, and it's unlikely this preference is in prefSet, so + // lets force log it. + logValue(key, value, true /* forceLog */); + } + return null; + } + } + + public class EditorLogger implements Editor { + @Override + public Editor putString(String key, @Nullable String value) { + safeLogValue(key, value); + return this; + } + + @Override + public Editor putStringSet(String key, @Nullable Set<String> values) { + safeLogValue(key, TextUtils.join(",", values)); + return this; + } + + @Override + public Editor putInt(String key, int value) { + logValue(key, value); + return this; + } + + @Override + public Editor putLong(String key, long value) { + logValue(key, value); + return this; + } + + @Override + public Editor putFloat(String key, float value) { + logValue(key, value); + return this; + } + + @Override + public Editor putBoolean(String key, boolean value) { + logValue(key, value); + return this; + } + + @Override + public Editor remove(String key) { + return this; + } + + @Override + public Editor clear() { + return this; + } + + @Override + public boolean commit() { + return true; + } + + @Override + public void apply() { + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java new file mode 100644 index 000000000000..79838962ef1e --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package com.android.settingslib.core.instrumentation; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import android.os.SystemClock; +import com.android.internal.logging.nano.MetricsProto; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; + +/** + * Logs visibility change of a fragment. + */ +public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause { + + private static final String TAG = "VisibilityLoggerMixin"; + + private final int mMetricsCategory; + + private MetricsFeatureProvider mMetricsFeature; + private int mSourceMetricsCategory = MetricsProto.MetricsEvent.VIEW_UNKNOWN; + private long mVisibleTimestamp; + + /** + * The metrics category constant for logging source when a setting fragment is opened. + */ + public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics"; + + private VisibilityLoggerMixin() { + mMetricsCategory = METRICS_CATEGORY_UNKNOWN; + } + + public VisibilityLoggerMixin(int metricsCategory, MetricsFeatureProvider metricsFeature) { + mMetricsCategory = metricsCategory; + mMetricsFeature = metricsFeature; + } + + @Override + public void onResume() { + mVisibleTimestamp = SystemClock.elapsedRealtime(); + if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { + mMetricsFeature.visible(null /* context */, mSourceMetricsCategory, mMetricsCategory); + } + } + + @Override + public void onPause() { + mVisibleTimestamp = 0; + if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) { + mMetricsFeature.hidden(null /* context */, mMetricsCategory); + } + } + + /** + * Sets source metrics category for this logger. Source is the caller that opened this UI. + */ + public void setSourceMetricsCategory(Activity activity) { + if (mSourceMetricsCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN || activity == null) { + return; + } + final Intent intent = activity.getIntent(); + if (intent == null) { + return; + } + mSourceMetricsCategory = intent.getIntExtra(EXTRA_SOURCE_METRICS_CATEGORY, + MetricsProto.MetricsEvent.VIEW_UNKNOWN); + } + + /** Returns elapsed time since onResume() */ + public long elapsedTimeSinceVisible() { + if (mVisibleTimestamp == 0) { + return 0; + } + return SystemClock.elapsedRealtime() - mVisibleTimestamp; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java new file mode 100644 index 000000000000..8bea51d1696d --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java @@ -0,0 +1,132 @@ +/* + * 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. + */ +package com.android.settingslib.core.instrumentation; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settingslib.TestConfig; +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class MetricsFeatureProviderTest { + private static int CATEGORY = 10; + private static boolean SUBTYPE_BOOLEAN = true; + private static int SUBTYPE_INTEGER = 1; + private static long ELAPSED_TIME = 1000; + + @Mock private LogWriter mockLogWriter; + @Mock private VisibilityLoggerMixin mockVisibilityLogger; + + private Context mContext; + private MetricsFeatureProvider mProvider; + + @Captor + private ArgumentCaptor<Pair> mPairCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mProvider = new MetricsFeatureProvider(); + List<LogWriter> writers = new ArrayList<>(); + writers.add(mockLogWriter); + ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers); + + when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME); + } + + @Test + public void logDashboardStartIntent_intentEmpty_shouldNotLog() { + mProvider.logDashboardStartIntent(mContext, null /* intent */, + MetricsEvent.SETTINGS_GESTURES); + + verifyNoMoreInteractions(mockLogWriter); + } + + @Test + public void logDashboardStartIntent_intentHasNoComponent_shouldLog() { + final Intent intent = new Intent(Intent.ACTION_ASSIST); + + mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); + + verify(mockLogWriter).action( + eq(mContext), + eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), + anyString(), + eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); + } + + @Test + public void logDashboardStartIntent_intentIsExternal_shouldLog() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES); + + verify(mockLogWriter).action( + eq(mContext), + eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK), + anyString(), + eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES))); + } + + @Test + public void action_BooleanLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } + + @Test + public void action_IntegerLogsElapsedTime() { + mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER); + verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture()); + + Pair value = mPairCaptor.getValue(); + assertThat(value.first instanceof Integer).isTrue(); + assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS); + assertThat(value.second).isEqualTo(ELAPSED_TIME); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java new file mode 100644 index 000000000000..d558a645aeb7 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java @@ -0,0 +1,181 @@ +/* + * 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. + */ +package com.android.settingslib.core.instrumentation; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Pair; + +import com.android.settingslib.TestConfig; +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import com.google.common.truth.Platform; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SharedPreferenceLoggerTest { + + private static final String TEST_TAG = "tag"; + private static final String TEST_KEY = "key"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + + private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher; + @Mock + private MetricsFeatureProvider mMetricsFeature; + private SharedPreferencesLogger mSharedPrefLogger; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature); + mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class); + } + + @Test + public void putInt_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 1); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + editor.putInt(TEST_KEY, 2); + + verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); + } + + @Test + public void putBoolean_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putBoolean(TEST_KEY, true); + editor.putBoolean(TEST_KEY, true); + editor.putBoolean(TEST_KEY, false); + editor.putBoolean(TEST_KEY, false); + editor.putBoolean(TEST_KEY, false); + + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true))); + verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false))); + } + + @Test + public void putLong_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, 2); + + verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class))); + } + + @Test + public void putLong_biggerThanIntMax_shouldLogIntMax() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + final long veryBigNumber = 500L + Integer.MAX_VALUE; + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, veryBigNumber); + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches( + FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE))); + } + + @Test + public void putLong_smallerThanIntMin_shouldLogIntMin() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + final long veryNegativeNumber = -500L + Integer.MIN_VALUE; + editor.putLong(TEST_KEY, 1); + editor.putLong(TEST_KEY, veryNegativeNumber); + + verify(mMetricsFeature).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches( + FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE))); + } + + @Test + public void putFloat_shouldNotLogInitialPut() { + final SharedPreferences.Editor editor = mSharedPrefLogger.edit(); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 1); + editor.putFloat(TEST_KEY, 2); + + verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(), + argThat(mNamePairMatcher), + argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class))); + } + + @Test + public void logPackage_shouldUseLogPackageApi() { + mSharedPrefLogger.logPackageName("key", "com.android.settings"); + verify(mMetricsFeature).action(any(Context.class), + eq(ACTION_SETTINGS_PREFERENCE_CHANGE), + eq("com.android.settings"), + any(Pair.class)); + } + + private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) { + return pair -> pair.first == tag && Platform.isInstanceOfType(pair.second, clazz); + } + + private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) { + return pair -> pair.first == tag + && Platform.isInstanceOfType(pair.second, Integer.class) + && pair.second.equals((bool ? 1 : 0)); + } + + private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) { + return pair -> pair.first == tag + && Platform.isInstanceOfType(pair.second, Integer.class) + && pair.second.equals(val); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java new file mode 100644 index 000000000000..a2648861d1d8 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java @@ -0,0 +1,122 @@ +/* + * 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. + */ +package com.android.settingslib.core.instrumentation; + +import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN; + +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class VisibilityLoggerMixinTest { + + @Mock + private MetricsFeatureProvider mMetricsFeature; + + private VisibilityLoggerMixin mMixin; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, mMetricsFeature); + } + + @Test + public void shouldLogVisibleOnResume() { + mMixin.onResume(); + + verify(mMetricsFeature, times(1)) + .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.VIEW_UNKNOWN), + eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldLogVisibleWithSource() { + final Intent sourceIntent = new Intent() + .putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY, + MetricsProto.MetricsEvent.SETTINGS_GESTURES); + final Activity activity = mock(Activity.class); + when(activity.getIntent()).thenReturn(sourceIntent); + mMixin.setSourceMetricsCategory(activity); + mMixin.onResume(); + + verify(mMetricsFeature, times(1)) + .visible(nullable(Context.class), eq(MetricsProto.MetricsEvent.SETTINGS_GESTURES), + eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldLogHideOnPause() { + mMixin.onPause(); + + verify(mMetricsFeature, times(1)) + .hidden(nullable(Context.class), eq(TestInstrumentable.TEST_METRIC)); + } + + @Test + public void shouldNotLogIfMetricsFeatureIsNull() { + mMixin = new VisibilityLoggerMixin(TestInstrumentable.TEST_METRIC, null); + mMixin.onResume(); + mMixin.onPause(); + + verify(mMetricsFeature, never()) + .hidden(nullable(Context.class), anyInt()); + } + + @Test + public void shouldNotLogIfMetricsCategoryIsUnknown() { + mMixin = new VisibilityLoggerMixin(METRICS_CATEGORY_UNKNOWN, mMetricsFeature); + + mMixin.onResume(); + mMixin.onPause(); + + verify(mMetricsFeature, never()) + .hidden(nullable(Context.class), anyInt()); + } + + private final class TestInstrumentable implements Instrumentable { + + public static final int TEST_METRIC = 12345; + + @Override + public int getMetricsCategory() { + return TEST_METRIC; + } + } +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 36981320ba5f..87ed7eb705e9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1122,6 +1122,12 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.ZRAM_ENABLED, GlobalSettingsProto.ZRAM_ENABLED); + dumpSetting(s, p, + Settings.Global.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS, + GlobalSettingsProto.ENABLE_SMART_REPLIES_IN_NOTIFICATIONS); + dumpSetting(s, p, + Settings.Global.SHOW_FIRST_CRASH_DIALOG, + GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG); } /** Dump a single {@link SettingsState.Setting} to a proto buf */ @@ -1513,6 +1519,9 @@ class SettingsProtoDumpUtil { Settings.Secure.ANR_SHOW_BACKGROUND, SecureSettingsProto.ANR_SHOW_BACKGROUND); dumpSetting(s, p, + Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + SecureSettingsProto.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION); + dumpSetting(s, p, Settings.Secure.VOICE_RECOGNITION_SERVICE, SecureSettingsProto.VOICE_RECOGNITION_SERVICE); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 175cff6b61b2..2a697b8d5034 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -60,6 +60,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; +import android.provider.SettingsValidators; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.text.TextUtils; @@ -297,6 +298,12 @@ public class SettingsProvider extends ContentProvider { @Override public boolean onCreate() { Settings.setInSystemServer(); + + // fail to boot if there're any backed up settings that don't have a non-null validator + ensureAllBackedUpSystemSettingsHaveValidators(); + ensureAllBackedUpGlobalSettingsHaveValidators(); + ensureAllBackedUpSecureSettingsHaveValidators(); + synchronized (mLock) { mUserManager = UserManager.get(getContext()); mPackageManager = AppGlobals.getPackageManager(); @@ -314,6 +321,57 @@ public class SettingsProvider extends ContentProvider { return true; } + private void ensureAllBackedUpSystemSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP, + Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS); + + failToBootIfOffendersPresent(offenders, "Settings.System"); + } + + private void ensureAllBackedUpGlobalSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP, + Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS); + + failToBootIfOffendersPresent(offenders, "Settings.Global"); + } + + private void ensureAllBackedUpSecureSettingsHaveValidators() { + String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP, + Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS); + + failToBootIfOffendersPresent(offenders, "Settings.Secure"); + } + + private void failToBootIfOffendersPresent(String offenders, String settingsType) { + if (offenders.length() > 0) { + throw new RuntimeException("All " + settingsType + " settings that are backed up" + + " have to have a non-null validator, but those don't: " + offenders); + } + } + + private String getOffenders(String[] settingsToBackup, Map<String, + SettingsValidators.Validator> validators) { + StringBuilder offenders = new StringBuilder(); + for (String setting : settingsToBackup) { + if (validators.get(setting) == null) { + offenders.append(setting).append(" "); + } + } + return offenders.toString(); + } + + private final String[] concat(String[] first, String[] second) { + if (second == null || second.length == 0) { + return first; + } + final int firstLen = first.length; + final int secondLen = second.length; + String[] both = new String[firstLen + secondLen]; + System.arraycopy(first, 0, both, 0, firstLen); + System.arraycopy(second, 0, both, firstLen, secondLen); + return both; + } + @Override public Bundle call(String method, String name, Bundle args) { final int requestingUserId = getRequestingUserId(args); @@ -1472,7 +1530,7 @@ public class SettingsProvider extends ContentProvider { } private void validateSystemSettingValue(String name, String value) { - Settings.System.Validator validator = Settings.System.VALIDATORS.get(name); + SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name); if (validator != null && !validator.validate(value)) { throw new IllegalArgumentException("Invalid value: " + value + " for setting: " + name); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0f43db052622..64b2ae6e23d3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -135,6 +135,9 @@ <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" /> <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" /> + <uses-permission android:name="android.permission.MANAGE_SENSORS" /> + <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" /> + <uses-permission android:name="android.permission.MANAGE_CAMERA" /> <application android:label="@string/app_label" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/Shell/res/values-eu/strings.xml b/packages/Shell/res/values-eu/strings.xml index 78295e62238b..9695e418ce7f 100644 --- a/packages/Shell/res/values-eu/strings.xml +++ b/packages/Shell/res/values-eu/strings.xml @@ -28,7 +28,7 @@ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Hautatu hau akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Sakatu akatsen txostena argazkirik gabe partekatzeko edo itxaron pantaila-argazkia atera arte"</string> - <string name="bugreport_confirm" msgid="5917407234515812495">"Errore-txostenek sistemaren erregistro-fitxategietako datuak dauzkate eta, haietan, isilpekotzat jotzen duzun informazioa ager daiteke (adibidez, aplikazioen erabilera eta kokapen-datuak). Errore-txostenak partekatzen badituzu, partekatu soilik pertsona eta aplikazio fidagarriekin."</string> + <string name="bugreport_confirm" msgid="5917407234515812495">"Errore-txostenek sistemaren erregistro-fitxategietako datuak dauzkate eta, haietan, kontuzkotzat jotzen duzun informazioa ager daiteke (adibidez, aplikazioen erabilera eta kokapen-datuak). Errore-txostenak partekatzen badituzu, partekatu soilik pertsona eta aplikazio fidagarriekin."</string> <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ez erakutsi berriro"</string> <string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string> <string name="bugreport_unreadable_text" msgid="586517851044535486">"Ezin izan da irakurri akatsen txostena"</string> diff --git a/packages/Shell/res/values-ne/strings.xml b/packages/Shell/res/values-ne/strings.xml index 8079210ebf12..eadfeb9155e8 100644 --- a/packages/Shell/res/values-ne/strings.xml +++ b/packages/Shell/res/values-ne/strings.xml @@ -23,9 +23,9 @@ <string name="bugreport_updating_title" msgid="4423539949559634214">"बग रिपोर्टमा विवरणहरू थप्दै"</string> <string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा गर्नुहोला..."</string> <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"उक्त बग सम्बन्धी रिपोर्ट चाँडै नै यस फोनमा देखा पर्नेछ"</string> - <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तपाईंको बग रिपोर्ट आदान-प्रदान गर्न चयन गर्नुहोस्"</string> + <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तपाईंको बग रिपोर्ट आदान प्रदान गर्न चयन गर्नुहोस्"</string> <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तपाईंको बग रिपोर्टलाई साझेदारी गर्न ट्याप गर्नुहोस्"</string> - <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तपाईंको बग रिपोर्ट स्क्रिनसट बिना आदान-प्रदान गर्नका लागि चयन गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुने प्रतीक्षा गर्नुहोस्"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तपाईंको बग रिपोर्ट स्क्रिनसट बिना आदान प्रदान गर्नका लागि चयन गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुने प्रतीक्षा गर्नुहोस्"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string> <string name="bugreport_confirm" msgid="5917407234515812495">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूको डेटा हुन्छ जसमा तपाईँले संवेदनशील मानेको डेटा समावेश हुन सक्छ (जस्तै अनुप्रयोगको प्रयोग र स्थान सम्बन्धी डेटा)। तपाईँले विश्वास गर्ने व्यक्ति र अनुप्रयोगहरूसँग मात्र बग रिपोर्टहरूलाई साझेदारी गर्नुहोस्।"</string> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index aa2cdbb7730f..80ac82576d13 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -121,7 +121,7 @@ <uses-permission android:name="android.permission.TRUST_LISTENER" /> <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" /> - <uses-permission android:name="android.permission.BIND_SLICE" /> + <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" /> <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked --> <uses-permission android:name="android.permission.SET_WALLPAPER"/> @@ -435,6 +435,16 @@ android:launchMode="singleTop" androidprv:alwaysFocusable="true" /> + <!-- started from SliceProvider --> + <activity android:name=".SlicePermissionActivity" + android:theme="@style/Theme.SystemUI.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true"> + <intent-filter> + <action android:name="android.intent.action.REQUEST_SLICE_PERMISSION" /> + </intent-filter> + </activity> + <!-- platform logo easter egg activity --> <activity android:name=".DessertCase" @@ -572,6 +582,7 @@ <provider android:name=".keyguard.KeyguardSliceProvider" android:authorities="com.android.systemui.keyguard" + android:grantUriPermissions="true" android:exported="true"> </provider> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 631cc0d3df30..41dd0b36d160 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -67,7 +67,7 @@ android:paddingTop="8dip" android:paddingBottom="8dip" android:paddingRight="0dp" - android:paddingLeft="24dp" + android:paddingLeft="0dp" android:background="@drawable/ripple_drawable" android:contentDescription="@string/keyboardview_keycode_delete" android:layout_alignEnd="@+id/pinEntry" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 947f27deb84e..33f7e750c390 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -81,7 +81,7 @@ android:paddingTop="8dip" android:paddingBottom="8dip" android:paddingRight="0dp" - android:paddingLeft="24dp" + android:paddingLeft="0dp" android:background="@drawable/ripple_drawable" android:contentDescription="@string/keyboardview_keycode_delete" android:layout_alignEnd="@+id/pinEntry" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 6f270b405c11..4b385fc3d6e1 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -82,7 +82,7 @@ android:paddingTop="8dip" android:paddingBottom="8dip" android:paddingRight="0dp" - android:paddingLeft="24dp" + android:paddingLeft="0dp" android:background="@drawable/ripple_drawable" android:contentDescription="@string/keyboardview_keycode_delete" android:layout_alignEnd="@+id/pinEntry" diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml index c19fa0803245..24afebde046b 100644 --- a/packages/SystemUI/res/color/qs_background_dark.xml +++ b/packages/SystemUI/res/color/qs_background_dark.xml @@ -15,6 +15,6 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.87" + <item android:alpha="1" android:color="?android:attr/colorBackgroundFloating"/> </selector> diff --git a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml deleted file mode 100644 index cde67976337e..000000000000 --- a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?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. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_landscape_to_rotate" - android:height="48dp" - android:width="48dp" - android:viewportHeight="48" - android:viewportWidth="48" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="device" - android:translateX="24" - android:translateY="24" > - <group - android:name="device_pivot" - android:translateX="-24.15" - android:translateY="-24.25" > - <group - android:name="landscape" - android:translateX="24" - android:translateY="24" > - <path - android:name="device_merged" - android:pathData="M -21.9799957275,-10.0 c 0.0,0.0 -0.0200042724609,20.0 -0.0200042724609,20.0 c 0.0,2.19999694824 1.80000305176,4.0 4.0,4.0 c 0.0,0.0 36.0,0.0 36.0,0.0 c 2.19999694824,0.0 4.0,-1.80000305176 4.0,-4.0 c 0.0,0.0 0.0,-20.0 0.0,-20.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 c 0.0,0.0 -36.0,0.0 -36.0,0.0 c -2.19999694824,0.0 -3.97999572754,1.80000305176 -3.97999572754,4.0 Z M 14.0,10.0 c 0.0,0.0 -28.0,0.0 -28.0,0.0 c 0.0,0.0 0.0,-20.0 0.0,-20.0 c 0.0,0.0 28.0,0.0 28.0,0.0 c 0.0,0.0 0.0,20.0 0.0,20.0 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - <group - android:name="arrows" - android:translateX="24" - android:translateY="24" - android:rotation="-90" > - <group - android:name="arrows_pivot" - android:translateX="-24.0798" - android:translateY="-24.23" > - <group - android:name="arrows_0" - android:translateX="12.2505" - android:translateY="37.2145" > - <path - android:name="bottom_merged" - android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml deleted file mode 100644 index b8465f4ba6b6..000000000000 --- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?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. ---> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_portrait_to_auto_rotate" > - <target - android:name="device_0" - android:animation="@anim/ic_portrait_to_rotate_device_0_animation" /> - <target - android:name="device_merged" - android:animation="@anim/ic_portrait_to_rotate_device_merged_animation" /> - <target - android:name="arrows" - android:animation="@anim/ic_portrait_to_rotate_arrows_animation" /> - <target - android:name="arrows_0" - android:animation="@anim/ic_portrait_to_rotate_arrows_0_animation" /> - <target - android:name="bottom_merged" - android:animation="@anim/ic_portrait_to_rotate_bottom_merged_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml deleted file mode 100644 index 0ef15f0582f7..000000000000 --- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?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. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_rotate_to_portrait" - android:height="48dp" - android:width="48dp" - android:viewportHeight="48" - android:viewportWidth="48" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="device" - android:translateX="24" - android:translateY="24" > - <group - android:name="device_pivot" - android:translateX="-24.15" - android:translateY="-24.25" > - <group - android:name="device_0" - android:translateX="24.14999" - android:translateY="24.25" - android:rotation="-135" > - <path - android:name="device_merged" - android:pathData="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - <group - android:name="arrows" - android:translateX="24" - android:translateY="24" - android:rotation="-221" > - <group - android:name="arrows_pivot" - android:translateX="-24.0798" - android:translateY="-24.23" > - <group - android:name="arrows_0" - android:translateX="12.2505" - android:translateY="37.2145" - android:scaleX="0.9" - android:scaleY="0.9" > - <path - android:name="bottom_merged" - android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml deleted file mode 100644 index 6d3fd372b222..000000000000 --- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?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. ---> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_portrait_from_auto_rotate" > - <target - android:name="device_0" - android:animation="@anim/ic_rotate_to_portrait_device_0_animation" /> - <target - android:name="device_merged" - android:animation="@anim/ic_rotate_to_portrait_device_merged_animation" /> - <target - android:name="arrows" - android:animation="@anim/ic_rotate_to_portrait_arrows_animation" /> - <target - android:name="arrows_0" - android:animation="@anim/ic_rotate_to_portrait_arrows_0_animation" /> - <target - android:name="bottom_merged" - android:animation="@anim/ic_rotate_to_portrait_bottom_merged_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml index bce494c975ed..fba45d17cc5b 100644 --- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml +++ b/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml @@ -1,6 +1,6 @@ <?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. @@ -16,12 +16,10 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_portrait_to_rotate" android:height="48dp" android:width="48dp" android:viewportHeight="48" - android:viewportWidth="48" - android:tint="?android:attr/colorControlNormal" > + android:viewportWidth="48"> <group android:name="device" android:translateX="24" @@ -61,4 +59,3 @@ </group> </group> </vector> - diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml index 5e7cd974818a..56223dbdb7c4 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml @@ -18,7 +18,7 @@ Copyright (C) 2014 The Android Open Source Project android:height="32dp" android:viewportWidth="6.0" android:viewportHeight="24.0" - android:tint="?android:attr/textColorSecondary"> + android:tint="?android:attr/colorAccent"> <path android:fillColor="#FFFFFFFF" android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/> diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml index 0dca1db32f88..12c1a7a89e32 100644 --- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml +++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml @@ -18,7 +18,7 @@ Copyright (C) 2014 The Android Open Source Project android:height="32dp" android:viewportWidth="6.0" android:viewportHeight="24.0" - android:tint="?android:attr/textColorSecondary"> + android:tint="?android:attr/colorAccent"> <path android:fillColor="#FFFFFFFF" android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/> diff --git a/packages/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml new file mode 100644 index 000000000000..d90129260fcc --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml @@ -0,0 +1,27 @@ +<!-- +Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT 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="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> + <path + android:pathData="M0 0h24v24H0z" + android:fillColor="#00000000"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml index 86dc6ce978bf..a1ad52841eb8 100644 --- a/packages/SystemUI/res/drawable/ic_landscape_to_auto_rotate_animation.xml +++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project +<!-- 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. @@ -14,16 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector +<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_landscape_to_auto_rotate" > - <target - android:name="landscape" - android:animation="@anim/ic_landscape_to_rotate_landscape_animation" /> - <target - android:name="arrows" - android:animation="@anim/ic_landscape_to_rotate_arrows_animation" /> - <target - android:name="bottom_merged" - android:animation="@anim/ic_landscape_to_rotate_bottom_merged_animation" /> -</animated-vector> + android:shape="rectangle"> + <gradient + android:angle="270" + android:startColor="#ff000000" + android:endColor="#00000000" + android:type="linear" /> +</shape> diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml index 59c0957d98cb..e52aa14abfa9 100644 --- a/packages/SystemUI/res/layout/battery_percentage_view.xml +++ b/packages/SystemUI/res/layout/battery_percentage_view.xml @@ -25,6 +25,6 @@ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" android:textColor="?android:attr/textColorPrimary" android:gravity="center_vertical|start" - android:paddingEnd="@dimen/battery_level_padding_start" + android:paddingStart="@dimen/battery_level_padding_start" android:importantForAccessibility="no" /> diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml index 3d0ab3599bf1..b96f44750a0c 100644 --- a/packages/SystemUI/res/layout/output_chooser.xml +++ b/packages/SystemUI/res/layout/output_chooser.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<!-- extends FrameLayout --> +<!-- extends LinearLayout --> <com.android.systemui.volume.OutputChooserLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" @@ -23,8 +23,17 @@ android:minHeight="320dp" android:layout_width="match_parent" android:layout_height="match_parent" + android:orientation="vertical" android:padding="20dp" > + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textDirection="locale" + android:textAppearance="@style/TextAppearance.QS.DetailHeader" + android:layout_marginBottom="20dp" /> + <com.android.systemui.qs.AutoSizingList android:id="@android:id/list" android:layout_width="match_parent" @@ -42,9 +51,10 @@ android:orientation="vertical"> <TextView - android:id="@android:id/title" + android:id="@+id/empty_text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:textDirection="locale" android:layout_marginTop="20dp" android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml index ed7df4b7f58a..c3ddbbe83dd7 100644 --- a/packages/SystemUI/res/layout/output_chooser_item.xml +++ b/packages/SystemUI/res/layout/output_chooser_item.xml @@ -30,6 +30,7 @@ android:layout_height="@dimen/qs_detail_item_icon_size" android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart" android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd" + android:background="?android:selectableItemBackgroundBorderless" android:tint="?android:attr/textColorPrimary"/> <LinearLayout diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml index 9ab8ac6346d3..b3b6a0c43a98 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel.xml @@ -20,6 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="0dp" + android:elevation="4dp" android:orientation="vertical" android:background="@drawable/qs_customizer_background" android:gravity="center_horizontal"> diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml index 1fd239b15b30..0b9a7b226105 100644 --- a/packages/SystemUI/res/layout/qs_detail.xml +++ b/packages/SystemUI/res/layout/qs_detail.xml @@ -23,7 +23,8 @@ android:clickable="true" android:orientation="vertical" android:paddingBottom="8dp" - android:visibility="invisible"> + android:visibility="invisible" + android:elevation="4dp" > <include android:id="@+id/qs_detail_header" diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 3d09b743ca88..9f6a946dea2e 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -21,6 +21,7 @@ android:id="@+id/qs_footer" android:layout_width="match_parent" android:layout_height="48dp" + android:elevation="4dp" android:baselineAligned="false" android:clickable="false" android:clipChildren="false" @@ -105,17 +106,6 @@ android:visibility="invisible"/> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> - - <com.android.systemui.statusbar.phone.ExpandableIndicator - android:id="@+id/expand_indicator" - android:layout_width="48dp" - android:layout_height="48dp" - android:clipToPadding="false" - android:clickable="true" - android:focusable="true" - android:background="?android:attr/selectableItemBackgroundBorderless" - android:contentDescription="@string/accessibility_quick_settings_expand" - android:padding="14dp" /> </LinearLayout> </com.android.systemui.qs.QSFooterImpl> diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 5541f3de8e7c..5bcb7fd2685e 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -18,17 +18,46 @@ android:id="@+id/quick_settings_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/qs_background_primary" - android:elevation="4dp" android:clipToPadding="false" - android:clipChildren="false"> + android:clipChildren="false" > + + <!-- Main QS background --> + <View + android:id="@+id/quick_settings_background" + android:layout_width="match_parent" + android:layout_height="0dp" + android:elevation="4dp" + android:background="@drawable/qs_background_primary" /> + + <!-- Black part behind the status bar --> + <View + android:id="@+id/quick_settings_status_bar_background" + android:layout_width="match_parent" + android:layout_height="@dimen/qs_header_system_icons_area_height" + android:clipToPadding="false" + android:clipChildren="false" + android:background="#ff000000" /> + + <!-- Gradient view behind QS --> + <View + android:id="@+id/quick_settings_gradient_view" + android:layout_width="match_parent" + android:layout_height="126dp" + android:layout_marginTop="@dimen/qs_header_system_icons_area_height" + android:clipToPadding="false" + android:clipChildren="false" + android:background="@drawable/qs_bg_gradient" /> + <com.android.systemui.qs.QSPanel android:id="@+id/quick_settings_panel" - android:layout_marginTop="28dp" + android:layout_marginTop="@dimen/qs_header_system_icons_area_height" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="48dp" /> + android:layout_marginBottom="48dp" + android:elevation="4dp" + /> + <include layout="@layout/quick_status_bar_expanded_header" /> diff --git a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate_animation.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 00bcaf6e2359..e0f0ed994166 100644 --- a/packages/SystemUI/res/drawable/ic_landscape_from_auto_rotate_animation.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project +<!-- 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. @@ -14,19 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector +<View xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_landscape_from_auto_rotate" > - <target - android:name="landscape" - android:animation="@anim/ic_rotate_to_landscape_landscape_animation" /> - <target - android:name="arrows" - android:animation="@anim/ic_rotate_to_landscape_arrows_animation" /> - <target - android:name="arrows_0" - android:animation="@anim/ic_rotate_to_landscape_arrows_0_animation" /> - <target - android:name="bottom_merged" - android:animation="@anim/ic_rotate_to_landscape_bottom_merged_animation" /> -</animated-vector> + android:id="@+id/quick_qs_status_icons" + android:layout_width="match_parent" + android:layout_height="20dp" + android:layout_marginBottom="22dp" + android:layout_below="@id/quick_status_bar_system_icons" + > + +</View> diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index e8b418cd902b..dacc3f96220b 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -28,23 +28,21 @@ android:clipToPadding="false" android:paddingTop="0dp" android:paddingEnd="0dp" - android:paddingStart="0dp"> + android:paddingStart="0dp" + android:elevation="4dp" > <include layout="@layout/quick_status_bar_header_system_icons" /> + <include layout="@layout/quick_qs_status_icons" /> <com.android.systemui.qs.QuickQSPanel android:id="@+id/quick_qs_panel" android:layout_width="match_parent" android:layout_height="48dp" - android:layout_alignParentEnd="true" - android:layout_marginTop="31dp" - android:layout_alignParentTop="true" + android:layout_below="@id/quick_qs_status_icons" android:accessibilityTraversalAfter="@+id/date_time_group" android:accessibilityTraversalBefore="@id/expand_indicator" android:clipChildren="false" android:clipToPadding="false" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" android:focusable="true" android:importantForAccessibility="yes" /> diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml index 739a2554e481..bfe1b6213c55 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml @@ -17,6 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/quick_status_bar_system_icons" android:layout_width="match_parent" android:layout_height="@dimen/qs_header_system_icons_area_height" android:layout_alignParentEnd="true" diff --git a/packages/SystemUI/res/layout/slice_permission_request.xml b/packages/SystemUI/res/layout/slice_permission_request.xml new file mode 100644 index 000000000000..cdb2a91a73d2 --- /dev/null +++ b/packages/SystemUI/res/layout/slice_permission_request.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Extends LinearLayout --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:orientation="vertical"> + + <TextView + android:id="@+id/text2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="8dp" + android:paddingStart="8dp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="@string/slice_permission_text_1" /> + + <TextView + android:id="@+id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="8dp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:paddingBottom="16dp" + android:text="@string/slice_permission_text_2" /> + + <CheckBox + android:id="@+id/slice_permission_checkbox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/slice_permission_checkbox" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 17b38cbce824..8c0b9bab35a0 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -54,30 +54,47 @@ android:layout_height="match_parent" android:layout="@layout/operator_name" /> - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:layout_width="wrap_content" + <LinearLayout android:layout_height="match_parent" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_left_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_left_clock_end_padding" - android:gravity="center_vertical|start" - /> + android:layout_width="0dp" + android:layout_weight="1" + > + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_left_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_left_clock_end_padding" + android:gravity="center_vertical|start" + /> + + <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and + PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout + android:id="@+id/notification_icon_area" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="horizontal" /> - <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and - PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> - <com.android.systemui.statusbar.AlphaOptimizedFrameLayout - android:id="@+id/notification_icon_area" - android:layout_width="0dip" + </LinearLayout> + + <!-- Space should cover the notch (if it exists) and let other views lay out around it --> + <android.widget.Space + android:id="@+id/cutout_space_view" + android:layout_width="0dp" android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="horizontal" /> + android:gravity="center_horizontal|center_vertical" + /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="match_parent" + android:layout_weight="1" android:orientation="horizontal" + android:gravity="center_vertical|end" > <include layout="@layout/system_icons" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index c5e5ee1f4af0..bef0830d5802 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -43,8 +43,6 @@ android:layout_width="@dimen/qs_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" - android:layout_marginStart="@dimen/notification_side_paddings" - android:layout_marginEnd="@dimen/notification_side_paddings" android:clipToPadding="false" android:clipChildren="false" systemui:viewType="com.android.systemui.plugins.qs.QS" /> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index bfa92ad89a1e..1fafb2fc72d6 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -16,12 +16,13 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/system_icons" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical"> - <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons" - android:layout_width="wrap_content" + <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" + android:layout_width="0dp" + android:layout_weight="1" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="horizontal"/> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 748c9a5ec7ed..5108f58dc832 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -34,7 +34,7 @@ <LinearLayout android:id="@+id/volume_dialog_rows" - android:layout_width="@dimen/volume_dialog_panel_width" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" @@ -56,6 +56,7 @@ android:layout_below="@id/volume_dialog_rows" android:background="@drawable/rounded_bg_full" android:gravity="center" + android:layout_gravity="end" android:orientation="vertical" > <TextView diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index fb756e308e19..8b2ab81c7fe8 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Voortdurend"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Kennisgewings"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is amper pap"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is amper pap. Skakel Batterybespaarder aan"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> oor"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> oor; ongeveer <xliff:g id="TIME">%s</xliff:g> oor op grond van jou gebruik"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> oor; ongeveer <xliff:g id="TIME">%s</xliff:g> oor"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> oor. Batterybespaarder is aan."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-laaiery nie ondersteun nie.\nGebruik net die laaier wat verskaf is."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Laai met USB word nie gesteun nie."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Die gebruiker wat tans by hierdie toestel aangemeld is, kan nie USB-ontfouting aanskakel nie. Skakel na die primêre gebruiker toe oor om hierdie kenmerk te gebruik."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoem om skerm te vul"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Strek om skerm te vul"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skermkiekie"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Stoor tans skermkiekie..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Stoor tans skermkiekie..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Skermkiekie word tans gestoor."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skermkiekie geneem."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tik om jou skermkiekie te sien."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Kon nie skermkiekie neem nie."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Kon nie skermkiekie stoor nie."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Skermkiekie word tans gestoor"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skermkiekie is gestoor"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tik om jou skermkiekie te bekyk"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Kon nie skermkiekie neem nie"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Kon nie skermkiekie stoor nie"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB-lêeroordrag-opsies"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Heg as \'n mediaspeler (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Af"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Met kragkennisgewingkontroles kan jy \'n belangrikheidvlak van 0 tot 5 vir \'n program se kennisgewings stel. \n\n"<b>"Vlak 5"</b>" \n- Wys aan die bokant van die kennisgewinglys \n- Laat volskermonderbreking toe \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 4"</b>" \n- Verhoed volskermonderbreking \n- Wys altyd opspringkennisgewings \n\n"<b>"Vlak 3"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n\n"<b>"Vlak 2"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n\n"<b>"Vlak 1"</b>" \n- Verhoed volskermonderbreking \n- Verhoed opspringkennisgewings \n- Moet nooit \'n klank maak of vibreer nie \n- Versteek van sluitskerm en statusbalk \n- Wys aan die onderkant van die kennisgewinglys \n\n"<b>"Vlak 0"</b>" \n- Blokkeer alle kennisgewings van die program af"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Kennisgewings"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Jy sal nie meer hierdie kennisgewings kry nie"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> kennisgewingkategorieë"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Hierdie program het nie kennisgewingkategorieë nie"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Kennisgewings van hierdie program af kan nie afgeskakel word nie"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 uit <xliff:g id="NUMBER_1">%s</xliff:g> kennisgewingkategorieë van hierdie program</item> - <item quantity="one">1 uit <xliff:g id="NUMBER_0">%s</xliff:g> kennisgewingkategorie van hierdie program</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> en <xliff:g id="NUMBER_5">%3$d</xliff:g> ander kanale</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> en <xliff:g id="NUMBER_2">%3$d</xliff:g> ander kanaal</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Jy sal nie meer hierdie kennisgewings sien nie"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Hou aan om hierdie kennisgewings te wys?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop kennisgewings"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Hou aan wys"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Hou aan om kennisgewings van hierdie program af te wys?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Hierdie kennisgewings kan nie afgeskakel word nie"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is oopgemaak"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is toegemaak"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Laat kennisgewings van hierdie kanaal af toe"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alle kategorieë"</string> <string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Pasmaak: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Pasmaak"</string> <string name="notification_done" msgid="5279426047273930175">"Klaar"</string> + <string name="inline_undo" msgid="558916737624706010">"Ontdoen"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kennisgewingkontroles"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"kennisgewing-sluimeropsies"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Vou uit"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimeer"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Maak toe"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Instellings"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Sleep af om toe te maak"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Kieslys"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 077131c7f63e..e548cae678d6 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"በመካሄድ ላይ ያለ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ማሳወቂያዎች"</string> <string name="battery_low_title" msgid="6456385927409742437">"የባትሪ ኃይል አነስተኛ ነው"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ባትሪ ዝቅተኛ ነው። የባትሪ ቆጣቢን ያብሩ"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል፣ በእርስዎ አጠቃቀም ላይ በመመረት <xliff:g id="TIME">%s</xliff:g> ገደማ ይቀራል"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል፣ <xliff:g id="TIME">%s</xliff:g> ገደማ ይቀራል"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ይቀራል። ባትሪ ቆጣቢ በርቷል።"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ኃይል መሙያ አይታገዝም።\n የቀረበውን ኃይል መሙያ ብቻ ተጠቀም።"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"የUSB ኃይል መሙላት አይደገፍም።"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"አሁን ወደዚህ መሣሪያ የገባው ተጠቃሚ የዩኤስቢ እርማትን ማብራት አይችልም። ይህን ባህሪ ለመጠቀም ወደ ዋና ተጠቃሚ ይቀይሩ።"</string> <string name="compat_mode_on" msgid="6623839244840638213">"ማያ እንዲሞላ አጉላ"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ማያ ለመሙለት ሳብ"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ቅጽበታዊ ገጽ እይታ"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ቅጽበታዊ ገጽ እይታ እየተቀመጠ ነው::"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ቅጽበታዊ ገጽ እይታ ተቀርጿል"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"የእርስዎን ቅጽበታዊ ገጽ እይታ ለማየት መታ ያድርጉ።"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ቅጽበታዊ ገጽ እይታ መቅረጽ አልተቻለም::"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ቅጽበታዊ ገጽ ዕይታን በማስቀመጥ ጊዜ ችግር አጋጥሟል።"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ባለው የተገደበ የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም።"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"ቅጽበታዊ ገጽ እይታ እየተቀመጠ ነው"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"ቅጽበታዊ ገጽ እይታ ተቀምጧል"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"የእርስዎን ቅጽበታዊ ገጽ እይታ ለማየት መታ ያድርጉ"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"ቅጽበታዊ ገጽ እይታን ማንሳት አልተቻለም"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ቅጽበታዊ ገጽ ዕይታን በማስቀመጥ ላይ ሳለ ችግር አጋጥሟል"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string> <string name="usb_preference_title" msgid="6551050377388882787">"የUSB ፋይል ሰደዳ አማራጮች"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"እንደ ማህደረ አጫዋች (MTP) ሰካ"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ጠፍቷል"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"በኃይል ማሳወቂያ መቆጣጠሪያዎች አማካኝነት የአንድ መተግበሪያ ማሳወቂያዎች የአስፈላጊነት ደረጃ ከ0 እስከ 5 ድረስ ማዘጋጀት ይችላሉ። \n\n"<b>"ደረጃ 5"</b>" \n- በማሳወቂያ ዝርዝሩ አናት ላይ አሳይ \n- የሙሉ ማያ ገጽ ማቋረጥን ፍቀድ \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 4"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- ሁልጊዜ አጮልቀው ይመልከቱ \n\n"<b>"ደረጃ 3"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ከልክል \n- በፍጹም አጮልቀው አይምልከቱ \n\n"<b>"ደረጃ 2"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ እና ንዝረትን በፍጹም አይኑር \n\n"<b>"ደረጃ 1"</b>" \n- የሙሉ ማያ ገጽ ማቋረጥን ይከልክሉ \n- በፍጹም አጮልቀው አይመልከቱ \n- ድምፅ ወይም ንዝረትን በፍጹም አያደርጉ \n- ከመቆለፊያ ገጽ እና የሁኔታ አሞሌ ይደብቁ \n- በማሳወቂያ ዝርዝር ግርጌ ላይ አሳይ \n\n"<b>"ደረጃ 0"</b>" \n- ሁሉንም የመተግበሪያው ማሳወቂያዎች ያግዱ"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"ማሳወቂያዎች"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"እነዚህን ማሳወቂያዎች ከእንግዲህ አያገኟቸውም"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> የማሳወቂያ ምድቦች"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ይህ መተግበሪያ የማሳወቂያ ምድቦች የሉትም"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ከዚህ መተግበሪያ የሚመጡ ማሳወቂያዎች ሊጠፉ አይችሉም"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 ከ<xliff:g id="NUMBER_1">%s</xliff:g> የማሳወቂያ ምድቦች ከዚህ መተግበሪያ</item> - <item quantity="other">1 <xliff:g id="NUMBER_1">%s</xliff:g> የማሳወቂያ ምድቦች ከዚህ መተግበሪያ</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> እና <xliff:g id="NUMBER_5">%3$d</xliff:g> ሌሎች</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>፣ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> እና <xliff:g id="NUMBER_5">%3$d</xliff:g> ሌሎች</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"እነዚህን ማሳወቂያዎችን ከእንግዲህ አይመለከቷቸውም"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"እነዚህን ማሳወቂያዎች ማሳየት ይቀጥሉ?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"ማሳወቂያዎችን አስቁም"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"ማሳየትን ቀጥል"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"ከዚህ መተግበሪያ ማሳወቂያዎችን ማሳየት ይቀጥል?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"እነዚህ ማሳወቂያዎች ሊጠፉ አይችሉም"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተከፍተዋል"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተዘግተዋል"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ከዚህ ሰርጥ የመጡ ሁሉንም ማሳወቂያች ፍቀድ"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ሁሉም ምድቦች"</string> <string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ያብጁ፦ <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"አብጅ"</string> <string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string> + <string name="inline_undo" msgid="558916737624706010">"ቀልብስ"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"የማሳወቂያ መቆጣጠሪያዎች"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"የማሳወቂያ ማሸለቢያ አማራጮች"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ዘርጋ"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"አሳንስ"</string> <string name="pip_phone_close" msgid="8416647892889710330">"ዝጋ"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ቅንብሮች"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ለማሰናበት ወደ ታች ይጎትቱ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"ምናሌ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> በስዕል-ላይ-ስዕል ውስጥ ነው"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index e5db8deeb095..a2a0f06f6411 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -37,7 +37,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"مستمر"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"الإشعارات"</string> <string name="battery_low_title" msgid="6456385927409742437">"البطارية منخفضة"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"طاقة البطارية منخفضة، لذا يُرجى تفعيل الوضع \"توفير شحن البطارية\"."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"متبقي <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"طاقة البطارية المتبقية <xliff:g id="PERCENTAGE">%s</xliff:g> ويتبقى على نفادها <xliff:g id="TIME">%s</xliff:g> تقريبًا بناءً على استخدامك."</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"طاقة البطارية المتبقية <xliff:g id="PERCENTAGE">%s</xliff:g> ويتبقى على نفادها <xliff:g id="TIME">%s</xliff:g> تقريبًا."</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"يتبقى <xliff:g id="PERCENTAGE">%s</xliff:g>. تم تفعيل ميزة توفير شحن البطارية."</string> <string name="invalid_charger" msgid="4549105996740522523">"شحن USB غير معتمد.\nاستخدم الشاحن الموفر فقط."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"لا يمكن إجراء الشحن عبر USB."</string> @@ -71,14 +74,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"لا يمكن للمستخدم الذي يسجّل دخوله حاليًا إلى هذا الجهاز تشغيل تصحيح أخطاء USB. لاستخدام هذه الميزة، يمكنك التبديل إلى المستخدم الأساسي."</string> <string name="compat_mode_on" msgid="6623839244840638213">"تكبير/تصغير لملء الشاشة"</string> <string name="compat_mode_off" msgid="4434467572461327898">"توسيع بملء الشاشة"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"لقطة شاشة"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"جارٍ حفظ لقطة الشاشة..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"جارٍ حفظ لقطة الشاشة..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"يتم حفظ لقطة."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"تم التقاط لقطة الشاشة"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"انقر لعرض لقطة الشاشة"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"تعذر التقاط لقطة الشاشة."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"حدثت مشكلة أثناء حفظ لقطة الشاشة."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"يتعذر حفظ لقطة الشاشة نظرًا لأن مساحة التخزين المتاحة محدودة."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"جارٍ حفظ لقطة الشاشة."</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"تم حفظ لقطة الشاشة."</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"انقر لعرض لقطة الشاشة."</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"تعذَّر الحصول على لقطة شاشة"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"حدثت مشكلة أثناء حفظ لقطة الشاشة."</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string> <string name="usb_preference_title" msgid="6551050377388882787">"خيارات نقل الملفات عبر USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"تحميل كمشغل وسائط (MTP)"</string> @@ -565,34 +569,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"إيقاف"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك تعيين مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة التأمين وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"الإشعارات"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"لن تتلقى هذه الإشعارات بعد الآن."</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> فئة إشعار"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"لا يحتوي هذا التطبيق على فئات إشعار"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"لا يمكن إيقاف الإشعارات من هذا التطبيق"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="zero">1 من إجمالي <xliff:g id="NUMBER_1">%s</xliff:g> فئة إشعار من هذا التطبيق</item> - <item quantity="two">1 من إجمالي فئتي إشعار (<xliff:g id="NUMBER_1">%s</xliff:g>) من هذا التطبيق</item> - <item quantity="few">1 من إجمالي <xliff:g id="NUMBER_1">%s</xliff:g> فئات إشعار من هذا التطبيق</item> - <item quantity="many">1 من إجمالي <xliff:g id="NUMBER_1">%s</xliff:g> فئة إشعار من هذا التطبيق</item> - <item quantity="other">1 من إجمالي <xliff:g id="NUMBER_1">%s</xliff:g> فئة إشعار من هذا التطبيق</item> - <item quantity="one">1 من إجمالي <xliff:g id="NUMBER_0">%s</xliff:g> فئة إشعار من هذا التطبيق</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="zero"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و<xliff:g id="NUMBER_5">%3$d</xliff:g> أيضًا</item> - <item quantity="two"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و<xliff:g id="NUMBER_5">%3$d</xliff:g> أيضًا</item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و<xliff:g id="NUMBER_5">%3$d</xliff:g> أيضًا</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و<xliff:g id="NUMBER_5">%3$d</xliff:g> أيضًا</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و<xliff:g id="NUMBER_5">%3$d</xliff:g> أيضًا</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g> و<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> و<xliff:g id="NUMBER_2">%3$d</xliff:g> أيضًا</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"لن تتلقى هذه الإشعارات بعد الآن."</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"هل تريد الاستمرار في تلقي هذه الإشعارات؟"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"إيقاف الإشعارات"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"الاستمرار في تلقّي الإشعارات"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"يتعذَّر إيقاف هذه الإشعارات."</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"تم فتح عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"تم إغلاق عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"السماح بالإشعارات من هذه القناة"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"كل الفئات"</string> <string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"تخصيص: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"تخصيص"</string> <string name="notification_done" msgid="5279426047273930175">"تم"</string> + <string name="inline_undo" msgid="558916737624706010">"تراجع"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"عناصر التحكم في الإشعارات"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"خيارات تأجيل الإشعارات"</string> @@ -755,8 +744,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"توسيع"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"تصغير"</string> <string name="pip_phone_close" msgid="8416647892889710330">"إغلاق"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"الإعدادات"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"اسحب لأسفل للإلغاء"</string> <string name="pip_menu_title" msgid="4707292089961887657">"القائمة"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> يظهر في صورة داخل صورة"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 1b2972bb22ca..e73cc86210eb 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Davam edir"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirişlər"</string> <string name="battery_low_title" msgid="6456385927409742437">"Enerji azdır"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batareya azdır. Batareya Qənaətini aktiv edin"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> qalır"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Qalan <xliff:g id="PERCENTAGE">%s</xliff:g>, istifadəyə əsasən təxminən <xliff:g id="TIME">%s</xliff:g> qalıb"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Qalan <xliff:g id="PERCENTAGE">%s</xliff:g>, təxminən <xliff:g id="TIME">%s</xliff:g> qalır"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> qalır. Batareya Qənaəti aktivdir."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ilə elektrik doldurma dəstəklənmir.\nYalnız adapter istifadə edin."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB qidalandırıcı dəstəklənmir."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Hazırda bu cihaza daxil olmuş istifadəçi USB sazlama prosesini aktiv edə bilməz. Bu funksiyadan istifadə etmək üçün əsas istifadəçi hesaba daxil olmalıdır."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Ekranı doldurmaq üçün yaxınlaşdır"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ekranı doldurmaq üçün uzat"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skrinşot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Skrinşot yadda saxlanılır..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Skrinşot yadda saxlanır..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Skrinşot yadda saxlanır."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skrinşot çəkildi."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Skrinşotunuza baxmaq üçün tıklayın."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Skrinşot götürülə bilinmədi."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Skrinşot yadda saxlanarkən problem baş verdi."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Skrinşot yadda saxlanır"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skrinşot yadda saxlandı"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Skrinşotunuza baxmaq üçün klikləyin"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Skrinşot çəkmək alınmadı"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Skrinşot yadda saxlanarkən problem baş verdi"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB fayl transferi seçimləri"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Media pleyer (MTP) kimi montaj edin"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Deaktiv"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Enerji bildiriş nəzarəti ilə, tətbiq bildirişləri üçün əhəmiyyət səviyyəsini 0-dan 5-ə kimi ayarlaya bilərsiniz. \n\n"<b>"Səviyyə 5"</b>" \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n- Tam ekran kəsintisinə icazə verin \n- Hər zaman izləyin \n\n"<b>"Səviyyə 4"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Hər zaman izləyin \n\n"<b>"Level 3"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n\n"<b>"Level 2"</b>" \n- Tam ekran kəsintisinin qarşısını alın \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n\n"<b>"Səviyyə 1"</b>" \n- Prevent full screen interruption \n- Heç vaxt izləməyin \n- Heç vaxt səsliyə və ya vibrasiyaya qoymayın \n- Ekran kilidi və ya status panelindən gizlədin \n- Bildiriş siyahısının yuxarı hissəsində göstərin \n\n"<b>"Səviyyə 0"</b>" \n- Bütün bildirişləri tətbiqdən blok edin"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirişlər"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Bu bildirişlər daha sizə göndərilməyəcək"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> bildiriş kateqoriyaları"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Bu tətbiqin bildiriş kateqoriyası yoxdur"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Bu tətbiqin bildirişləri deaktiv edilə bilməz"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Bu tətbiqin <xliff:g id="NUMBER_1">%s</xliff:g> bildiriş kateqoriyasından 1 kanal</item> - <item quantity="one">Bu tətbiqin <xliff:g id="NUMBER_0">%s</xliff:g> bildiriş kateqoriyasından 1 kanal</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, və <xliff:g id="NUMBER_5">%3$d</xliff:g> digər</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, və <xliff:g id="NUMBER_2">%3$d</xliff:g> digər</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Artıq bu bildirişləri görməyəcəkəsiniz"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirişlər göstərilməyə davam edilsin?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Bildirişləri dayandırın"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Göstərməyə davam edin"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu tətbiqin bildirişləri göstərilməyə davam edilsin?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirişlər deaktiv edilə bilməz"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları açıqdır"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları bağlıdır"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Bu kanaldan gələn bildirişlərə icazə verin"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Bütün Kateqoriyalar"</string> <string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Fərdiləşdirin: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Fərdiləşdirin"</string> <string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string> + <string name="inline_undo" msgid="558916737624706010">"Ləğv edin"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"bildiriş nəzarəti"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"bildiriş təxirə salma seçimləri"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Genişləndirin"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Kiçildin"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Bağlayın"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Ayarlar"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Rədd etmək üçün aşağı çəkin"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menyu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 2d3635c13fbd..3c203b1fccd1 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Tekuće"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obaveštenja"</string> <string name="battery_low_title" msgid="6456385927409742437">"Nivo napunjenosti baterije je nizak"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterija je skoro prazna. Uključite Uštedu baterije"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>, na osnovu korišćenja ostalo je oko <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>, ostalo je oko <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>. Ušteda baterije je uključena."</string> <string name="invalid_charger" msgid="4549105996740522523">"Punjenje preko USB-a nije podržano.\nKoristite samo priloženi punjač."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Punjenje preko USB-a nije podržano."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutno prijavljen na ovaj uređaj ne može da uključi otklanjanje grešaka na USB-u. Da biste koristili ovu funkciju, prebacite na primarnog korisnika."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj na celom ekranu"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Razvuci na ceo ekran"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snimak ekrana"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Čuvanje snimka ekrana..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Čuvanje snimka ekrana..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Snimak ekrana se čuva."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Snimak ekrana je napravljen."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Dodirnite da biste videli snimak ekrana."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nije moguće napraviti snimak ekrana."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Došlo je do problema pri čuvanju snimka ekrana."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Snimak ekrana se čuva"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Snimak ekrana je sačuvan"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Dodirnite da biste videli snimak ekrana"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ne možete da napravite snimak ekrana"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Došlo je do problema pri čuvanju snimka ekrana"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prenosa datoteka"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Priključi kao medija plejer (MTP)"</string> @@ -559,28 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Pomoću naprednih kontrola za obaveštenja možete da podesite nivo važnosti od 0. do 5. za obaveštenja aplikacije. \n\n"<b>"5. nivo"</b>" \n– Prikazuju se u vrhu liste obaveštenja \n- Dozvoli prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"4. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Uvek zaviruj \n\n"<b>"3. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n\n"<b>"2. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n\n"<b>"1. nivo"</b>" \n– Spreči prekid režima celog ekrana \n– Nikada ne zaviruj \n– Nikada ne proizvodi zvuk ili vibraciju \n– Sakrij na zaključanom ekranu i statusnoj traci \n– Prikazuju se u dnu liste obaveštenja \n\n"<b>"0. nivo"</b>" \n– Blokiraj sva obaveštenja iz aplikacije"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Obaveštenja"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Više nećete dobijati ova obaveštenja"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Kategorija obaveštenja: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ova aplikacija nema kategorije obaveštenja"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Obaveštenja iz ove aplikacije ne mogu da se isključe"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obaveštenja za ovu aplikaciju</item> - <item quantity="few">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obaveštenja za ovu aplikaciju</item> - <item quantity="other">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorija obaveštenja za ovu aplikaciju</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Više nećete videti ova obaveštenja"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Želite li da se ova obaveštenja i dalje prikazuju?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Prestani da prikazuješ obaveštenja"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Nastavi da prikazuješ"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obaveštenja iz ove aplikacije i dalje prikazuju?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ne možete da isključite ova obaveštenja"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrole obaveštenja za otvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrole obaveštenja za zatvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Dozvoli obaveštenja sa ovog kanala"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Sve kategorije"</string> <string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Prilagodite: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Prilagodi"</string> <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string> + <string name="inline_undo" msgid="558916737624706010">"Opozovi"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrole obaveštenja"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcije za odlaganje obaveštenja"</string> @@ -737,8 +732,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Umanji"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Podešavanja"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Prevucite nadole da biste odbili"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meni"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 27e3a7d5f555..41ce8239e9ed 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Пастаянныя"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Апавяшчэнні"</string> <string name="battery_low_title" msgid="6456385927409742437">"Нізкі ўзровень зараду акумулятара"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Нізкі зарад акумулятара. Уключыце функцыю эканоміі зараду"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Засталося <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Засталося <xliff:g id="PERCENTAGE">%s</xliff:g>, у вас ёсць каля <xliff:g id="TIME">%s</xliff:g> на аснове даных аб выкарыстанні вашай прылады"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Засталося <xliff:g id="PERCENTAGE">%s</xliff:g>, у вас ёсць каля <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Засталося <xliff:g id="PERCENTAGE">%s</xliff:g>. Уключана эканомія зараду."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-зарадка не падтрымліваецца.\nКарыстайцеся толькі зарадкай для прылады."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Зарадка па USB не падтрымліваецца."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Карыстальнік, які зараз увайшоў у гэту прыладу, не можа ўключыць адладку USB. Каб выкарыстоўваць гэту функцыю, пераключыцеся на асноўнага карыстальніка."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Павял. на ўвесь экран"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Расцягн. на ўвесь экран"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Здымак экрана"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Захаванне скрыншота..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Захаванне скрыншота..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Скрыншот захаваны."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Скрыншот зроблены"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Дакраніцеся, каб прагледзець здымак экрана."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Не атрымалася зрабiць скрыншот."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Падчас захавання скрыншота адбылася памылка."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Немагчыма захаваць здымак экрана, бо мала месца ў памяці."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Захаванне здымка экрана"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Здымак экрана захаваны"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Дакраніцеся, каб прагледзець здымак экрана"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Не атрымалася зрабіць здымак экрана"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Падчас захавання здымка экрана адбылася памылка"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчу"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Парам. перадачы файлаў па USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Падлучыць як медыяпрайгравальнік (ССП)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Выключана"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"З дапамогай пашыранага кіравання апавяшчэннямі вы можаце задаваць узровень важнасці апавяшчэнняў праграмы ад 0 да 5. \n\n"<b>"Узровень 5"</b>" \n- Паказваць уверсе спіса апавяшчэнняў \n- Дазваляць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 4"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Заўсёды дазваляць кароткі паказ \n\n"<b>"Узровень 3"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n\n"<b>"Узровень 2"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n\n"<b>"Узровень 1"</b>" \n- Забараняць перапыняць рэжым поўнага экрана \n- Ніколі не дазваляць кароткі паказ \n- Ніколі не прайграваць гук і не вібрыраваць \n- Хаваць з экрана блакіроўкі і панэлі стану \n- Паказваць унізе спіса апавяшчэнняў \n\n"<b>"Узровень 0"</b>" \n- Блакіраваць усе апавяшчэнні ад праграмы"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Апавяшчэнні"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Вы больш не будзеце атрымліваць гэтыя апавяшчэнні"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Катэгорый апавяшчэнняў: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"У гэтай праграме няма катэгорый апавяшчэнняў"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Апавяшчэнні ад гэтай праграмы нельга адключыць"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 з <xliff:g id="NUMBER_1">%s</xliff:g> катэгорыі апавяшчэнняў у гэтай праграме</item> - <item quantity="few">1 з <xliff:g id="NUMBER_1">%s</xliff:g> катэгорый апавяшчэнняў у гэтай праграме</item> - <item quantity="many">1 з <xliff:g id="NUMBER_1">%s</xliff:g> катэгорый апавяшчэнняў у гэтай праграме</item> - <item quantity="other">1 з <xliff:g id="NUMBER_1">%s</xliff:g> катэгорыі апавяшчэнняў у гэтай праграме</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і яшчэ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і яшчэ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і яшчэ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і яшчэ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Вы больш не будзеце бачыць гэты апавяшчэнні"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Працягваць паказваць гэтыя апавяшчэнні?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Спыніць апавяшчэнні"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Працягваць паказваць"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Працягваць паказваць апавяшчэнні гэтай праграмы?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Немагчыма адключыць гэты апавяшчэнні"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> адкрыта"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> закрыта"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Дазволіць апавяшчэнні з гэтага канала"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Усе катэгорыі"</string> <string name="notification_more_settings" msgid="816306283396553571">"Дадатковыя налады"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Персаналізаваць: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Наладзіць"</string> <string name="notification_done" msgid="5279426047273930175">"Гатова"</string> + <string name="inline_undo" msgid="558916737624706010">"Адрабіць"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"кіраванне апавяшчэннямі"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"параметры адкладвання апавяшчэнняў"</string> @@ -745,8 +738,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Разгарнуць"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Згарнуць"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Закрыць"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Налады"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Перацягніце ўніз, каб адхіліць"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> з’яўляецца відарысам у відарысе"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 6a93d9e0a7f2..fbdf93cfc7ff 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"В момента"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Известия"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батерията е изтощена"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батерията е изтощена – включете режима за запазването й"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Остава/т <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Остава/т <xliff:g id="PERCENTAGE">%s</xliff:g> – още около <xliff:g id="TIME">%s</xliff:g> въз основа на използването"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Остава/т <xliff:g id="PERCENTAGE">%s</xliff:g> – още около <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Остава/т <xliff:g id="PERCENTAGE">%s</xliff:g>. Режимът за запазване на батерията е включен."</string> <string name="invalid_charger" msgid="4549105996740522523">"Не се поддържа зареждане през USB.\nИзползвайте само доставеното зарядно устройство."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Зареждането през USB не се поддържа."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Потребителят, който понастоящем е влязъл в това устройство, не може да включи функцията за отстраняване на грешки през USB. За да я използвате, превключете към основния потребител."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Мащаб – запълва екрана"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Разпъване – запълва екрана"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Екранна снимка"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Екранната снимка се запазва..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Екранната снимка се запазва..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Екранната снимка се запазва."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Екранната снимка е заснета."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Докоснете, за да видите екранната снимка."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Екранната снимка не можа да бъде заснета."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"При запазването на екранната снимка възникна проблем."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Екранната снимка не може да се запази поради ограничено място в хранилището."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Екранната снимка се запазва"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Екранната снимка е запазена"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Докоснете, за да видите екранната снимка"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Екранната снимка не можа да бъде направена"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"При запазването на екранната снимка възникна проблем"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Екранната снимка не може да се запази поради ограничено място в хранилището"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Правенето на екранни снимки не е разрешено от приложението или организацията ви"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Опции за пренос на файлове чрез USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Свързване като медиен плейър (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Изкл."</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"С помощта на контролите за известията можете да зададете ниво на важност от 0 до 5 за известията от дадено приложение. \n\n"<b>"Ниво 5"</b>" \n– Показване най-горе в списъка с известия. \n– Разрешаване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 4"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията винаги се показват мимолетно. \n\n"<b>"Ниво 3"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n\n"<b>"Ниво 2"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n\n"<b>"Ниво 1"</b>" \n– Предотвратяване на прекъсването на цял екран. \n– Известията никога не се показват мимолетно. \n– Без издаване на звуков сигнал и вибриране. \n– Скриване от заключения екран и лентата на състоянието. \n– Показване най-долу в списъка с известия. \n\n"<b>"Ниво 0"</b>" \n– Блокиране на всички известия от приложението."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Известия"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Вече няма да получавате тези известия"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> категории известия"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"За това приложение няма категории на известията"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Известията от това приложение не могат да бъдат изключени"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 от <xliff:g id="NUMBER_1">%s</xliff:g> категории известия от това приложение</item> - <item quantity="one">1 от <xliff:g id="NUMBER_0">%s</xliff:g> категория известия от това приложение</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"„<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>“"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ и още <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one">„<xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>“ и още <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Вече няма да виждате тези известия"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Тези известия да продължат ли да се показват?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Спиране на известията"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Да продължат да се показват"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Да продължат ли да се показват известията от това приложение?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Тези известия не могат да бъдат изключени"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са оттворени"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са затворени"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Разрешаване на известия от този канал"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Всички категории"</string> <string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Персонализиране: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Персонализиране"</string> <string name="notification_done" msgid="5279426047273930175">"Готово"</string> + <string name="inline_undo" msgid="558916737624706010">"Отмяна"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"контроли за известията"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"опции за отлагане на известията"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Разгъване"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Намаляване"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Затваряне"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Настройки"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Преместете надолу с плъзгане, за да отхвърлите"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> е в режима „Картина в картината“"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 4b163ec9b5d4..a8e0c2b1430c 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"চলতে-থাকা"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তিগুলি"</string> <string name="battery_low_title" msgid="6456385927409742437">"ব্যাটারি কম"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে। ব্যাটারি সেভার চালু আছে।"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB চার্জিং সমর্থিত নয়৷\nকেবলমাত্র সরবহারকৃত চার্জার ব্যবহার করুন৷"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB চার্জিং সমর্থিত নয়।"</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ব্যবহারকারী এখন এই ডিভাইসে সাইন-ইন করেছেন তাই USB ডিবাগিং চালু করা যাবে না। এই বৈশিষ্ট্যটি ব্যবহার করতে, প্রাথমিক ব্যবহারকারীতে পাল্টে নিন।"</string> <string name="compat_mode_on" msgid="6623839244840638213">"স্ক্রীণ পূরণ করতে জুম করুন"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ফুল স্ক্রিন করুন"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"স্ক্রিনশট সেভ করা হচ্ছে..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"স্ক্রিনশট সেভ করা হচ্ছে..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"স্ক্রিনশট সেভ করা হচ্ছে৷"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"স্ক্রিনশট নেওয়া হযেছে৷"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"আপনার স্ক্রিনশট দেখতে আলতো চাপ দিন৷"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"স্ক্রিনশট নেওয়া যায়নি৷"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"স্ক্রিনশট সেভের সময়ে সমস্যা হয়েছে৷"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"স্টোরেজ সীমিত থাকায় স্ক্রিনশটটি সেভ করা যাবে না৷"</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ফাইল স্থানান্তরের বিকল্পগুলি"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"একটি মিডিয়া প্লেয়ার হিসেবে মাউন্ট করুন (MTP)"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"বন্ধ আছে"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"পাওয়ার বিজ্ঞপ্তির নিয়ন্ত্রণগুলি ব্যহবার করে, আপনি কোনও অ্যাপ্লিকেশনের বিজ্ঞপ্তির জন্য ০ থেকে ৫ পর্যন্ত একটি গুরুত্বের লেভেলকে সেট করতে পারবেন৷ \n\n"<b>"লেভেল ৫"</b>" \n- বিজ্ঞপ্তি তালিকার শীর্ষে দেখায় \n- পূর্ণ স্ক্রিনের বাধাকে অনুমতি দেয় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৪"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- সর্বদা স্ক্রিনে উপস্থিত হয় \n\n"<b>"লেভেল ৩"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n\n"<b>"লেভেল ২"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n\n"<b>"লেভেল ১"</b>" \n- পূর্ণ স্ক্রিনের বাধাকে আটকায় \n- কখনওই স্ক্রিনে উপস্থিত হয় না \n- কখনওই শব্দ এবং কম্পন করে না \n- লক স্ক্রিন এবং স্ট্যাটাস বার থেকে লুকায় \n- বিজ্ঞপ্তি তালিকার নীচের দিকে দেখায় \n\n"<b>"লেভেল ০"</b>" \n- অ্যাপ্লিকেশন থেকে সমস্ত বিজ্ঞপ্তিকে অবরূদ্ধ করে"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"বিজ্ঞপ্তি"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"আপনি এই বিজ্ঞপ্তিগুলি আর পাবেন না"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> বিজ্ঞপ্তির বিভাগগুলি"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"এই অ্যাপটিতে বিজ্ঞপ্তির বিভাগ নেই"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"এই অ্যাপ থেকে আসা বিজ্ঞপ্তি বন্ধ করা যাবে না"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">এই অ্যাপের <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তির বিভাগের মধ্যে ১টি</item> - <item quantity="other">এই অ্যাপের <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তির বিভাগের মধ্যে ১টি</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, এবং আরও <xliff:g id="NUMBER_5">%3$d</xliff:g>টি</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, এবং আরও <xliff:g id="NUMBER_5">%3$d</xliff:g>টি</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> খোলা থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"এই চ্যানেল থেকে বিজ্ঞপ্তি আসতে দেয়"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"সকল বিভাগ"</string> <string name="notification_more_settings" msgid="816306283396553571">"আরও সেটিংস"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"কাস্টমাইজ করুন: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"বিজ্ঞপ্তি মনে করিয়ে দেওয়ার বিকল্পগুলি"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"বড় করুন"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ছোটো করুন"</string> <string name="pip_phone_close" msgid="8416647892889710330">"বন্ধ করুন"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"সেটিংস"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"খারিজ করতে নিচের দিকে টেনে আনুন"</string> <string name="pip_menu_title" msgid="4707292089961887657">"মেনু"</string> <string name="pip_notification_title" msgid="3204024940158161322">"ছবির-মধ্যে-ছবি তে <xliff:g id="NAME">%s</xliff:g> আছেন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index fc246c406159..69b8fbed5342 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"U toku"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obavještenja"</string> <string name="battery_low_title" msgid="6456385927409742437">"Baterija je skoro prazna"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterija je skoro prazna. Uključite Uštedu baterije"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Preostalo <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>. Preostalo je oko <xliff:g id="TIME">%s</xliff:g>, na osnovu vašeg korištenja"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>. Preostalo je oko <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Preostalo <xliff:g id="PERCENTAGE">%s</xliff:g>. Uključena je Ušteda baterije."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB punjenje nije podržano.\nKoristite samo priloženi punjač."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Punjenje pomoću USB-a nije podržano."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutno prijavljen na ovaj uređaj ne može uključiti opciju za otklanjanje grešaka koristeći USB. Da koristite tu funkciju, prebacite se na primarnog korisnika."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Uvećaj prikaz na ekran"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Razvuci prikaz na ekran"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snimak ekrana"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Spašavanje snimka ekrana..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Spašavanje snimka ekrana..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Spašavanje snimka ekrana u toku."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran snimljen."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Dodirnite za prikaz snimka ekrana."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Došlo je do greške prilikom snimanja ekrana."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Došlo je do problema prilikom spašavanja snimka ekrana."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Snimak ekrana se čuva"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Snimak ekrana je sačuvan"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Dodirnite za prikaz snimka ekrana"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Pravljenje snimka ekrana nije uspjelo"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Došlo je do problema prilikom čuvanja snimka ekrana"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prijenosa fajlova"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Reproduciranje medijskih sadržaja (MTP)"</string> @@ -561,28 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Uz kontrolu obavještenja o napajanju, možete postaviti nivo značaja obavještenja iz aplikacije, i to od nivoa 0 do 5. \n\n"<b>"Nivo 5"</b>" \n- Prikaži na vrhu liste obavještenja \n- Dopusti prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nvio 4"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Uvijek izviruj \n\n"<b>"Nivo 3"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n\n"<b>"Nivo 2"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikad ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n\n"<b>"Nivo 1"</b>" \n- Spriječi prekid prikaza cijelog ekrana \n- Nikada ne izviruj \n- Nikada ne puštaj zvuk ili vibraciju \n- Sakrij sa ekrana za zaključavanje i statusne trake \n- Prikaži na dnu liste obavještenja \n\n"<b>"Nivo 0"</b>" \n- Blokiraj sva obavještenja iz aplikacije"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Obavještenja"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Nećete više primati ova obavještenja"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Kategorije obavještenja: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ova aplikacija nema kategorije obavještenja"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Obavještenja iz ove aplikacije nije moguće isključiti"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obavještenja iz ove aplikacije</item> - <item quantity="few">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obavještenja iz ove aplikacije</item> - <item quantity="other">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorija obavještenja iz ove aplikacije</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Nećete više vidjeti ova obavještenja"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Nastaviti prikazivanje ovih obavještenja?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Zaustavi obavještenja"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivanje"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Nastaviti prikazivanje obavještenja iz ove aplikacije?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ova obavještenja nije moguće isključiti"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Otvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Zatvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Dozvoli obavještenja s ovog kanala"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Sve kategorije"</string> <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Prilagodite: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Prilagodi"</string> <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string> + <string name="inline_undo" msgid="558916737624706010">"Opozovi"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrole obavještenja"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcije za odgodu obavještenja"</string> @@ -739,8 +734,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Umanji"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Postavke"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povucite prema dolje da odbacite"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meni"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 4ff8e2c55deb..d61d15e6c4c7 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continu"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificacions"</string> <string name="battery_low_title" msgid="6456385927409742437">"Queda poca bateria"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda poca bateria. Activa el mode Estalvi de bateria."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>."</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>; temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>; temps restant aproximat: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>. La funció Estalvi de bateria està activada."</string> <string name="invalid_charger" msgid="4549105996740522523">"Pujada d\'USB no admesa.\nUtilitza només el carregador proporcionat."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"La pujada per USB no és compatible."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració per USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom per omplir pantalla"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Estira per omplir pant."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"S\'està desant captura de pantalla..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"S\'està desant la captura de pantalla..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"La captura de pantalla s\'ha desat."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"S\'ha fet una captura de pantalla."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toca la notificació per veure la captura de pantalla."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"No s\'ha pogut fer una captura de pantalla."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"S\'ha trobat un problema en desar la captura de pantalla."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"S\'està desant la captura de pantalla"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"S\'ha desat la captura de pantalla"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toca per veure la captura de pantalla"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"No s\'ha pogut fer la captura de pantalla"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"S\'ha trobat un problema en desar la captura de pantalla"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opcions transf. fitxers USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Munta com a reproductor multimèdia (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivat"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Amb els controls de notificació millorats, pots establir un nivell d\'importància d\'entre 0 i 5 per a les notificacions d\'una aplicació. \n\n"<b>"Nivell 5"</b>" \n- Mostra les notificacions a la part superior de la llista \n- Permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 4"</b>" \n- No permet la interrupció de la pantalla completa \n- Permet sempre la previsualització \n\n"<b>"Nivell 3"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n\n"<b>"Nivell 2"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- Les notificacions no poden emetre sons ni vibracions \n\n"<b>"Nivell 1"</b>" \n- No permet la interrupció de la pantalla completa \n- No permet mai la previsualització \n- No activa mai el so ni la vibració \n- Amaga les notificacions de la pantalla de bloqueig i de la barra d\'estat \n- Mostra les notificacions a la part inferior de la llista \n\n"<b>"Nivell 0"</b>" \n- Bloqueja totes les notificacions de l\'aplicació"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacions"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ja no rebràs aquestes notificacions"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categories de notificació"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Aquesta aplicació no té categories de notificació"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Les notificacions d\'aquesta aplicació no es poden desactivar"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categories de notificació d\'aquesta aplicació</item> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificació d\'aquesta aplicació</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i <xliff:g id="NUMBER_5">%3$d</xliff:g> més</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> i <xliff:g id="NUMBER_2">%3$d</xliff:g> més</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ja no veuràs aquestes notificacions"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Vols continuar rebent aquestes notificacions?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Deixa d\'enviar notificacions"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continua rebent"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Aquestes notificacions no es poden desactivar"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"S\'han obert els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"S\'han tancat els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permet les notificacions d\'aquest canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Totes les categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalitza: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalitza"</string> <string name="notification_done" msgid="5279426047273930175">"Fet"</string> + <string name="inline_undo" msgid="558916737624706010">"Desfés"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controls de notificació"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcions per posposar la notificació"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Desplega"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimitza"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Tanca"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Configuració"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrossega cap avall per ignorar-ho"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> està en pantalla en pantalla"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9c7de6a447a1..bb0d362e638d 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string> <string name="battery_low_title" msgid="6456385927409742437">"Baterie je slabá"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterie je téměř vybitá, zapněte spořič baterie"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>, při obvyklém využití asi <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>, asi <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g>. Spořič baterie je zapnutý."</string> <string name="invalid_charger" msgid="4549105996740522523">"Nabíjení pomocí rozhraní USB není podporováno.\nPoužívejte pouze nabíječku, která byla dodána se zařízením."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Nabíjení přes USB není podporováno."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Přiblížit na celou obrazovku"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Na celou obrazovku"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Ukládání snímku obrazovky..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Ukládání snímku obrazovky..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Probíhá ukládání snímku obrazovky."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Snímek obrazovky pořízen"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Klepnutím zobrazíte snímek obrazovky."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Snímek obrazovky se nepodařilo zachytit."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Při ukládání snímku obrazovky došlo k problému."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snímek obrazovky nelze pořídit kvůli nedostatku místa v úložišti."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Ukládání snímku obrazovky"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Snímek obrazovky byl uložen"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Klepnutím snímek obrazovky zobrazíte"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Snímek obrazovky se nepodařilo zachytit"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Při ukládání snímku obrazovky došlo k problému"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Možnosti přenosu souborů pomocí rozhraní USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Připojit jako přehrávač médií (MTP)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Vypnuto"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Rozšířené ovládací prvky oznámení umožňují nastavit úroveň důležitosti oznámení aplikace od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazit na začátku seznamu oznámení \n– Povolit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 4"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Vždy zobrazit náhled \n\n"<b>"Úroveň 3"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n\n"<b>"Úroveň 2"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat žádný zvukový signál ani nevibrovat \n\n"<b>"Úroveň 1"</b>" \n– Zabránit vyrušení na celou obrazovku \n– Nikdy nezobrazovat náhled \n– Nikdy nevydávat zvukový signál ani nevibrovat \n– Skrýt z obrazovky uzamčení a stavového řádku \n– Zobrazovat na konci seznamu oznámení \n\n"<b>";Úroveň 0"</b>" \n– Blokovat všechna oznámení z aplikace"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Oznámení"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Tato oznámení již nebudete dostávat"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Kategorie oznámení: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Tato aplikace nemá kategorie oznámení"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Oznámení této aplikace nelze vypnout"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="few">1 ze <xliff:g id="NUMBER_1">%s</xliff:g> kategorií oznámení z této aplikace</item> - <item quantity="many">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorie oznámení z této aplikace</item> - <item quantity="other">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorií oznámení z této aplikace</item> - <item quantity="one">1 z <xliff:g id="NUMBER_0">%s</xliff:g> kategorie oznámení z této aplikace</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> další</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> dalšího</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> dalších</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> a <xliff:g id="NUMBER_2">%3$d</xliff:g> další</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Tato oznámení již nebudete dostávat"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Mají se tato oznámení nadále zobrazovat?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Přestat zobrazovat oznámení"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Nadále zobrazovat"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Mají se oznámení z této aplikace nadále zobrazovat?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tato oznámení nelze deaktivovat"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly otevřeny"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly zavřeny"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Povolit oznámení z tohoto kanálu"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Všechny kategorie"</string> <string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Přizpůsobit: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Upravit"</string> <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string> + <string name="inline_undo" msgid="558916737624706010">"Zpět"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"Nastavení oznámení"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Možnosti odložení oznámení"</string> @@ -745,8 +738,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Rozbalit"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizovat"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zavřít"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Nastavení"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Nápovědu zavřete přetažením dolů"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Nabídka"</string> <string name="pip_notification_title" msgid="3204024940158161322">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 543843456aaa..e2ff065f3672 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Underretninger"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batteriniveauet er lavt"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batteriniveauet er lavt – aktivér Batterisparefunktion"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> tilbage"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Der er <xliff:g id="PERCENTAGE">%s</xliff:g> tilbage eller ca. <xliff:g id="TIME">%s</xliff:g>, alt efter hvordan du bruger enheden"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> tilbage eller ca. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> tilbage. Batterisparefunktion er aktiveret."</string> <string name="invalid_charger" msgid="4549105996740522523">"Opladning via USB understøttes ikke.\nBrug kun den medfølgende oplader."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB-opladning understøttes ikke."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Den bruger, der i øjeblikket er logget ind på denne enhed, kan ikke aktivere USB-fejlretning. Skift til den primære bruger for at bruge denne funktion."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom til fuld skærm"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stræk til fuld skærm"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Gemmer screenshot..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Gemmer screenshot..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshottet gemmes."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshottet er gemt."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tryk for at se dit screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshottet kunne ikke tages."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Der opstod et problem ved lagringen af screenshottet."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Screenshottet kan ikke gemmes pga. begrænset lagerplads."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshottet gemmes"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshottet blev gemt"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tryk for at se dit screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Screenshottet kunne ikke tages"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Der opstod et problem, da screenshottet skulle gemmes"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Appen eller din organisation tillader ikke, at du tager screenshots"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Muligheder for USB-filoverførsel"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Isæt som en medieafspiller (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Fra"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Med kontrolelementer til underretninger om strøm kan du konfigurere et vigtighedsniveau fra 0 til 5 for en apps underretninger. \n\n"<b>"Niveau 5"</b>\n"- Vis øverst på listen over underretninger \n- Tillad afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 4"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se altid smugkig \n\n"<b>"Niveau 3"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n\n"<b>"Niveau 2"</b>\n"- Ingen afbrydelse af fuld skærm \n Se aldrig smugkig \n- Ingen lyd og vibration \n\n"<b>"Niveau 1"</b>\n"- Ingen afbrydelse af fuld skærm \n- Se aldrig smugkig \n- Ingen lyd eller vibration \n- Skjul fra låseskærm og statusbjælke \n- Vis nederst på listen over underretninger \n\n"<b>"Niveau 0"</b>\n"- Bloker alle underretninger fra appen."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Underretninger"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Du modtager ikke længere disse underretninger"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> underretningskategorier"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Denne app har ingen underretningskategorier"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Underretninger fra denne app kan ikke deaktiveres"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 ud af <xliff:g id="NUMBER_1">%s</xliff:g> underretningskategori fra denne app</item> - <item quantity="other">1 ud af <xliff:g id="NUMBER_1">%s</xliff:g> underretningskategorier fra denne app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> anden</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> andre</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Du får ikke længere vist disse underretninger"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsætte med at se disse underretninger?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop underretninger"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Fortsæt med at vise underretninger"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se underretninger fra denne app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Disse underretninger kan ikke deaktiveres"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Styring af underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Styring af underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g> blev lukket"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillad underretninger fra denne kanal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alle kategorier"</string> <string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Tilpas: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Tilpas"</string> <string name="notification_done" msgid="5279426047273930175">"Udfør"</string> + <string name="inline_undo" msgid="558916737624706010">"Fortryd"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrolelementer til underretninger"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Indstillinger for udsættelse"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a68ad2adcd46..53bf7d83f540 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string> <string name="battery_low_title" msgid="6456385927409742437">"Akku ist schwach"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akku ist schwach. Energiesparmodus aktivieren."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%s</xliff:g>, basierend auf deiner Nutzung"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwende das mitgelieferte Aufladegerät."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Laden per USB wird nicht unterstützt."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Der momentan auf diesem Gerät angemeldete Nutzer kann das USB-Debugging nicht aktivieren. Um diese Funktion verwenden zu können, wechsle zum primären Nutzer."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom auf Bildschirmgröße"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Auf Bildschirmgröße anpassen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Screenshot wird gespeichert..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot wird gespeichert..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wird gespeichert..."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot aufgenommen"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tippe, um deinen Screenshot anzusehen."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot konnte nicht aufgenommen werden."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Beim Speichern des Screenshots ist ein Problem aufgetreten."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot wird gespeichert"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot gespeichert"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tippe, um deinen Screenshot anzusehen"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Screenshot konnte nicht aufgenommen werden"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Beim Speichern des Screenshots ist ein Problem aufgetreten"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB-Dateiübertragungsoptionen"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Als Medienplayer (MTP) bereitstellen"</string> @@ -561,26 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Aus"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Mit den erweiterten Benachrichtigungseinstellungen kannst du für App-Benachrichtigungen eine Wichtigkeitsstufe von 0 bis 5 festlegen. \n\n"<b>"Stufe 5"</b>" \n- Auf der Benachrichtigungsleiste ganz oben anzeigen \n- Vollbildunterbrechung zulassen \n- Immer kurz einblenden \n\n"<b>"Stufe 4"</b>" \n- Keine Vollbildunterbrechung \n- Immer kurz einblenden \n\n"<b>"Stufe 3"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n\n"<b>"Stufe 2"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n\n"<b>"Stufe 1"</b>" \n- Keine Vollbildunterbrechung \n- Nie kurz einblenden \n- Weder Ton noch Vibration \n- Auf Sperrbildschirm und Statusleiste verbergen \n- Auf der Benachrichtigungsleiste ganz unten anzeigen \n\n"<b>"Stufe 0"</b>" \n- Alle Benachrichtigungen der App sperren"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Benachrichtigungen"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Du erhältst diese Benachrichtigungen nicht mehr"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> Benachrichtigungskategorien"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Diese App hat keine Benachrichtigungskategorien"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Benachrichtigungen von dieser App können nicht deaktiviert werden"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 von <xliff:g id="NUMBER_1">%s</xliff:g> Benachrichtigungskategorien von dieser App</item> - <item quantity="one">1 von <xliff:g id="NUMBER_0">%s</xliff:g> Benachrichtigungskategorie von dieser App</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> und <xliff:g id="NUMBER_5">%3$d</xliff:g> andere</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> und <xliff:g id="NUMBER_2">%3$d</xliff:g> andere</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Du erhältst diese Benachrichtigungen nicht mehr"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Diese Benachrichtigungen weiterhin anzeigen?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Benachrichtigungen nicht mehr anzeigen"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Weiterhin anzeigen"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Benachrichtigungen dieser App weiterhin anzeigen?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Diese Benachrichtigungen können nicht deaktiviert werden"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geöffnet"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geschlossen"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Benachrichtigungen von diesem Kanal zulassen"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alle Kategorien"</string> <string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Anpassen: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Anpassen"</string> <string name="notification_done" msgid="5279426047273930175">"Fertig"</string> + <string name="inline_undo" msgid="558916737624706010">"Rückgängig machen"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> – <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"Benachrichtigungseinstellungen"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Optionen für spätere Erinnerung bei Benachrichtigungen"</string> @@ -735,8 +732,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Maximieren"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimieren"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Schließen"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Einstellungen"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zum Schließen nach unten ziehen"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 7464d31b401d..663191c6bc1d 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string> <string name="battery_low_title" msgid="6456385927409742437">"Χαμηλή στάθμη μπαταρίας"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Χαμηλή στάθμη μπαταρίας. Ενεργοποιήστε την Εξοικονόμηση μπαταρίας"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Απομένουν <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Απομένει <xliff:g id="PERCENTAGE">%s</xliff:g>, περίπου <xliff:g id="TIME">%s</xliff:g> με βάση τη χρήση σας"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Απομένει <xliff:g id="PERCENTAGE">%s</xliff:g>, περίπου <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Απομένουν <xliff:g id="PERCENTAGE">%s</xliff:g>. Η Εξοικονόμηση μπαταρίας είναι ενεργή."</string> <string name="invalid_charger" msgid="4549105996740522523">"Δεν υποστηρίζεται η φόρτιση USB.\nΧρησιμοποιείτε μόνο τον φορτιστή που παρέχεται."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Δεν υποστηρίζεται η φόρτιση μέσω USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ο χρήστης που είναι συνδεδεμένος αυτήν τη στιγμή σε αυτήν τη συσκευή δεν μπορεί να ενεργοποιήσει τον εντοπισμό σφαλμάτων USB. Για να χρησιμοποιήσετε αυτήν τη λειτουργία, κάντε εναλλαγή στον κύριο χρήστη."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Ζουμ σε πλήρη οθόνη"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Προβoλή σε πλήρη οθ."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Στιγμιότυπο οθόνης"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Αποθήκ. στιγμιότυπου οθόνης..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Αποθήκευση στιγμιότυπου οθόνης..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Γίνεται αποθήκευση του στιγμιότυπου οθόνης."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Λήφθηκε το στιγμιότυπο οθόνης ."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Πατήστε για να δείτε το στιγμιότυπο οθόνης που δημιουργήσατε."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Παρουσιάστηκε πρόβλημα κατά την αποθήκευση του στιγμιότυπου οθόνης."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Δεν είναι δυνατή η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου χώρου αποθήκευσης."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Γίνεται αποθήκευση του στιγμιότυπου οθόνης"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Το στιγμιότυπο οθόνης αποθηκεύτηκε"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Πατήστε για να δείτε το στιγμιότυπο οθόνης που δημιουργήσατε"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Αδύνατη η λήψη του στιγμιότυπου οθόνης."</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Παρουσιάστηκε πρόβλημα κατά την αποθήκευση του στιγμιότυπου οθόνης"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Επιλογές μεταφοράς αρχείων μέσω USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Προσάρτηση ως μονάδας αναπαραγωγής μέσων (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Ανενεργή"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Με τα στοιχεία ελέγχου ειδοποίησης ισχύος, μπορείτε να ορίσετε ένα επίπεδο βαρύτητας από 0 έως 5 για τις ειδοποιήσεις μιας εφαρμογής. \n\n"<b>"Επίπεδο 5"</b>" \n- Εμφάνιση στην κορυφή της λίστας ειδοποιήσεων \n- Να επιτρέπεται η διακοπή πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 4"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να γίνεται πάντα σύντομη προβολή \n\n"<b>"Επίπεδο 3"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n\n"<b>"Επίπεδο 2"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n\n"<b>"Επίπεδο 1"</b>" \n- Αποτροπή διακοπής πλήρους οθόνης \n- Να μην γίνεται ποτέ σύντομη προβολή \n- Να μην χρησιμοποιείται ποτέ ήχος και δόνηση \n- Απόκρυψη από την οθόνη κλειδώματος και τη γραμμή κατάστασης \n- Εμφάνιση στο κάτω μέρος της λίστας ειδοποιήσεων \n\n"<b>"Επίπεδο 0"</b>" \n- Αποκλεισμός όλων των ειδοποιήσεων από την εφαρμογή"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Ειδοποιήσεις"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Δεν θα λαμβάνετε πλέον αυτές τις ειδοποιήσεις"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> κατηγορίες ειδοποιήσεων"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Αυτή η εφαρμογή δεν διαθέτει κατηγορίες ειδοποιήσεων"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Οι ειδοποιήσεις από αυτήν την εφαρμογή δεν μπορούν να απενεργοποιηθούν"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 από <xliff:g id="NUMBER_1">%s</xliff:g> κατηγορίες ειδοποιήσεων από αυτή την εφαρμογή</item> - <item quantity="one">1 από <xliff:g id="NUMBER_0">%s</xliff:g> κατηγορία ειδοποιήσεων από αυτή την εφαρμογή</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> και <xliff:g id="NUMBER_5">%3$d</xliff:g> ακόμη</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> και <xliff:g id="NUMBER_2">%3$d</xliff:g> ακόμη</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Δεν θα βλέπετε πλέον αυτές τις ειδοποιήσεις"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Να συνεχίσουν να εμφανίζονται αυτές οι ειδοποιήσεις;"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Διακοπή ειδοποιήσεων"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Συνέχιση εμφάνισης"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Να συνεχίσουν να εμφανίζονται ειδοποιήσεις από αυτήν την εφαρμογή;"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Αδύνατη η απενεργοποίηση αυτών των ειδοποιήσεων"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> άνοιξαν"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> έκλεισαν"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Να επιτρέπονται οι ειδοποιήσεις από αυτό το κανάλι"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Όλες οι κατηγορίες"</string> <string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Προσαρμογή: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Προσαρμογή"</string> <string name="notification_done" msgid="5279426047273930175">"Τέλος"</string> + <string name="inline_undo" msgid="558916737624706010">"Αναίρεση"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"στοιχεία ελέγχου ειδοποιήσεων"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"επιλογές αφύπνισης ειδοποιήσεων"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Ανάπτυξη"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Ελαχιστοποίηση"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Κλείσιμο"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Ρυθμίσεις"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Σύρετε προς τα κάτω για παράβλεψη"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Μενού"</string> <string name="pip_notification_title" msgid="3204024940158161322">"Η λειτουργία picture-in-picture είναι ενεργή σε <xliff:g id="NAME">%s</xliff:g>."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index b1b1626b4db5..ab759ed0c560 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string> <string name="notification_done" msgid="5279426047273930175">"Done"</string> + <string name="inline_undo" msgid="558916737624706010">"Undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Close"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 420dd9d3cd97..1bd101fcc278 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string> <string name="notification_done" msgid="5279426047273930175">"Done"</string> + <string name="inline_undo" msgid="558916737624706010">"Undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Close"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index b1b1626b4db5..ab759ed0c560 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string> <string name="notification_done" msgid="5279426047273930175">"Done"</string> + <string name="inline_undo" msgid="558916737624706010">"Undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Close"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index b1b1626b4db5..ab759ed0c560 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Customise: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Customise"</string> <string name="notification_done" msgid="5279426047273930175">"Done"</string> + <string name="inline_undo" msgid="558916737624706010">"Undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expand"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimise"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Close"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Settings"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Drag down to dismiss"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 4cc96c17bf87..ad391464809b 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining. Battery Saver is on."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB charging not supported.\nUse only the supplied charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB charging not supported."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom to fill screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stretch to fill screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot is being saved."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tap to view your screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organization"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Mount as a media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications. \n\n"<b>"Level 5"</b>" \n- Show at the top of the notification list \n- Allow full screen interruption \n- Always peek \n\n"<b>"Level 4"</b>" \n- Prevent full screen interruption \n- Always peek \n\n"<b>"Level 3"</b>" \n- Prevent full screen interruption \n- Never peek \n\n"<b>"Level 2"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound and vibration \n\n"<b>"Level 1"</b>" \n- Prevent full screen interruption \n- Never peek \n- Never make sound or vibrate \n- Hide from lock screen and status bar \n- Show at the bottom of the notification list \n\n"<b>"Level 0"</b>" \n- Block all notifications from the app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"You won\'t get these notifications anymore"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> notification categories"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"This app doesn\'t have notification categories"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifications from this app can\'t be turned off"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="one">1 out of <xliff:g id="NUMBER_0">%s</xliff:g> notification category from this app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_2">%3$d</xliff:g> other</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"You won\'t see these notifications anymore"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Keep showing these notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stop notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Keep showing"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"These notifications can\'t be turned off"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> closed"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Allow notifications from this channel"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"All Categories"</string> <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Customize: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Customize"</string> <string name="notification_done" msgid="5279426047273930175">"Done"</string> + <string name="inline_undo" msgid="558916737624706010">"Undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 690168659f9f..cdb432bb17ef 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batería baja"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda poca batería. Activa el Ahorro de batería"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de batería."</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Batería: <xliff:g id="PERCENTAGE">%s</xliff:g> (tiempo restante aproximado según tu uso: <xliff:g id="TIME">%s</xliff:g>)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Batería: <xliff:g id="PERCENTAGE">%s</xliff:g> (tiempo restante aproximado: <xliff:g id="TIME">%s</xliff:g>)"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Queda <xliff:g id="PERCENTAGE">%s</xliff:g> de batería. El Ahorro de batería está activado."</string> <string name="invalid_charger" msgid="4549105996740522523">"No admite la carga USB.\nUsa sólo el cargador provisto."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"No se admite la carga por USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"El usuario al que accediste en este dispositivo no puede activar la depuración por USB. Para usar esta función, debes cambiar al usuario principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ocupar la pantalla"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Estirar p/ ocupar la pantalla"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Guardando captura de pantalla"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Guardando la captura de pantalla..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"La captura de pantalla se está guardando."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Se guardó la captura de pantalla."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Presiona para ver tu captura de pantalla."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"No se pudo guardar la captura de pantalla."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Se produjo un error al guardar la captura de pantalla."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"No se puede guardar la captura de pantalla debido al almacenamiento limitado."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Se está guardando la captura de pantalla"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Se guardó la captura de pantalla"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Presiona para ver la captura de pantalla"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"No se pudo tomar la captura de pantalla"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Se produjo un error al guardar la captura de pantalla"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"La app o tu organización no permiten las capturas de pantalla"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Activar como reproductor de medios (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivado"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Con los controles de activación de notificaciones, puedes establecer un nivel de importancia para las notificaciones de una app. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones. \n- Permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 4"</b>" \n- No permitir interrupción en la pantalla completa. \n- Mostrar siempre. \n\n"<b>"Nivel 3"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n\n"<b>"Nivel 2"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n\n"<b>"Nivel 1"</b>" \n- No permitir interrupción en la pantalla completa. \n- No mostrar. \n- No sonar ni vibrar. \n- Ocultar de la pantalla bloqueada y la barra de estado. \n- Mostrar al final de la lista de notificaciones. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la app."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ya no recibirás estas notificaciones"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorías de notificaciones"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta app no tiene categorías de notificación"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"No es posible desactivar las notificaciones de esta app"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorías de notificación de esta app</item> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoría de notificación de esta app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> y <xliff:g id="NUMBER_5">%3$d</xliff:g> más</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> y <xliff:g id="NUMBER_2">%3$d</xliff:g> más</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ya no verás estas notificaciones"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"¿Quieres seguir viendo estas notificaciones?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Detener notificaciones"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Seguir viendo"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"No se pueden desactivar estas notificaciones"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Se abrieron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Se cerraron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir las notificaciones de este canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas las categorías"</string> <string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Listo"</string> + <string name="inline_undo" msgid="558916737624706010">"Deshacer"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificación"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opciones para posponer notificaciones"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Cerrar"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Configuración"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastra hacia abajo para descartar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está en modo de imagen en imagen"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index a5d5c4427841..b467b389ecf8 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Entrante"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string> <string name="battery_low_title" msgid="6456385927409742437">"Nivel de batería bajo"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda poca batería. Activa la función Ahorro de batería"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de batería"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> (tiempo restante aproximado según tu uso: <xliff:g id="TIME">%s</xliff:g>)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> (tiempo restante aproximado: <xliff:g id="TIME">%s</xliff:g>)"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de batería. Se ha activado la función Ahorro de energía."</string> <string name="invalid_charger" msgid="4549105996740522523">"No se admite la carga por USB.\nUtiliza solo el cargador proporcionado."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"No se admite la carga por USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"El usuario con el que se ha iniciado sesión en este dispositivo no puede activar la depuración USB. Para utilizar esta función, inicia sesión con la cuenta de usuario principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ajustar"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Expandir para ajustar"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Guardando captura..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Guardando captura..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"La captura de pantalla se está guardando."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura guardada"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toca para ver la captura de pantalla."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"No se ha podido guardar la captura de pantalla."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Se ha detectado un problema al guardar la captura de pantalla."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"La captura de pantalla se está guardando"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Se ha guardado la captura de pantalla"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toca para ver la captura de pantalla"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"No se ha podido guardar la captura de pantalla"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"No se ha podido guardar la captura de pantalla"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Activar como reproductor de medios (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivado"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Los controles de energía de las notificaciones permiten establecer un nivel de importancia de 0 a 5 para las notificaciones de las aplicaciones. \n\n"<b>"Nivel 5"</b>" \n- Mostrar en la parte superior de la lista de notificaciones \n- Permitir interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 4"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- Mostrar siempre \n\n"<b>"Nivel 3"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n\n"<b>"Nivel 2"</b>" \n- Evitar interrumpir en el modo de pantalla completa\n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n\n"<b>"Nivel 1"</b>" \n- Evitar interrumpir en el modo de pantalla completa \n- No mostrar nunca \n- No emitir sonido ni vibrar nunca \n- Ocultar de la pantalla de bloqueo y de la barra de estado \n- Mostrar en la parte inferior de la lista de notificaciones \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas las notificaciones de la aplicación"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificaciones"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ya no recibirás estas notificaciones"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorías de notificación"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicación no tiene categorías de notificación"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"No se pueden desactivar las notificaciones de esta aplicación"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorías de notificación de esta aplicación</item> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoría de notificación de esta aplicación</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> y <xliff:g id="NUMBER_5">%3$d</xliff:g> más</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> y <xliff:g id="NUMBER_2">%3$d</xliff:g> más</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"No volverás a ver estas notificaciones"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"¿Quieres seguir viendo estas notificaciones?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Detener las notificaciones"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Seguir mostrando"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta aplicación?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Estas notificaciones no se pueden desactivar"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Se han abierto los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Se han cerrado los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permite las notificaciones de este canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas las categorías"</string> <string name="notification_more_settings" msgid="816306283396553571">"Más ajustes"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Listo"</string> + <string name="inline_undo" msgid="558916737624706010">"Deshacer"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"Controles de las notificaciones"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Opciones para posponer las notificaciones"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 84cbc444bead..1b8ad6530ed0 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Jätkuv"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Märguanded"</string> <string name="battery_low_title" msgid="6456385927409742437">"Aku hakkab tühjaks saama"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Aku hakkab tühjaks saama. Lülitage sisse akusäästja"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Jäänud on <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> on alles, teie kasutuse põhjal on jäänud umbes <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> on alles, umbes <xliff:g id="TIME">%s</xliff:g> on jäänud"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Jäänud on <xliff:g id="PERCENTAGE">%s</xliff:g>. Akusäästja on sisse lülitatud."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB laadimist ei toetata.\nKasutage ainult tootja laadija."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB-ga laadimist ei toetata."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Sellesse seadmesse praegu sisse logitud kasutaja ei saa USB-silumist sisse lülitada. Selle funktsiooni kasutamiseks vahetage peamisele kasutajale."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Suumi ekraani täitmiseks"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Venita ekraani täitmiseks"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ekraanipilt"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Kuvatõmmise salvestamine ..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Kuvatõmmise salvestamine ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Kuvatõmmist salvestatakse."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekraanipilt on jäädvustatud."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Puudutage ekraanipildi vaatamiseks."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Kuvatõmmist ei saanud jäädvustada."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Ekraanipildi salvestamisel ilmnes probleem."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Ekraanipilti salvestatakse"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Ekraanipilt salvestati"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Puudutage ekraanipildi vaatamiseks"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ekraanipilti ei saanud jäädvustada"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Ekraanipildi salvestamisel ilmnes probleem"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB-failiedastuse valikud"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Paigalda meediumimängijana (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Väljas"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Toite märguannete juhtnuppudega saate määrata rakenduse märguannete tähtsuse taseme vahemikus 0–5. \n\n"<b>"5. tase"</b>" \n- Kuva märguannete loendi ülaosas\n- Luba täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"4. tase"</b>" \n- Keela täisekraanil häirimine \n- Kuva alati ekraani servas \n\n"<b>"3. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n\n"<b>"2. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n\n"<b>"1. tase"</b>" \n- Keela täisekraanil häirimine \n- Ära kunagi kuva ekraani servas \n- Ära kunagi helise ega vibreeri \n- Peida lukustuskuval ja olekuribal \n- Kuva märguannete loendi allosas \n\n"<b>"Tase 0"</b>" \n- Blokeeri kõik rakenduse märguanded"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Märguanded"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Te ei saa enam neid märguandeid"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> märguandekategooriat"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Sellel rakendusel ei ole märguannete kategooriaid"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Selle rakenduse märguandeid ei saa välja lülitada"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 <xliff:g id="NUMBER_1">%s</xliff:g>-st märguannete kategooriast sellelt rakenduselt</item> - <item quantity="one">1 <xliff:g id="NUMBER_0">%s</xliff:g>-st märguannete kategooriast sellelt rakenduselt</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ja veel <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalit</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> ja veel <xliff:g id="NUMBER_2">%3$d</xliff:g> kanal</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Te ei näe enam neid märguandeid"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Kas soovite nende märguannete kuvamist jätkata?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Peata märguanded"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Jätka kuvamist"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Kas jätkata selle rakenduse märguannete kuvamist?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Neid märguandeid ei saa välja lülitada"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on avatud"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on suletud"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Lubab selle kanali märguanded"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Kõik kategooriad"</string> <string name="notification_more_settings" msgid="816306283396553571">"Rohkem seadeid"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Kohandamine: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Kohandamine"</string> <string name="notification_done" msgid="5279426047273930175">"Valmis"</string> + <string name="inline_undo" msgid="558916737624706010">"Võta tagasi"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"märguannete juhtnupud"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"märguannete edasilükkamise valikud"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Laiendamine"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimeeri"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Sule"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Seaded"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Loobumiseks lohistage alla"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menüü"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 85d3c6e3d434..1d49b3be15b9 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Abian"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Jakinarazpenak"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateria agortzen ari da"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Bateria gutxi gelditzen da. Aktibatu Bateria-aurrezlea."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da; <xliff:g id="TIME">%s</xliff:g> inguru gelditzen dira, erabileraren arabera"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da; <xliff:g id="TIME">%s</xliff:g> inguru gelditzen dira"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> gelditzen da. Bateria-aurrezlea aktibatuta dago."</string> <string name="invalid_charger" msgid="4549105996740522523">"Ez da USB bidez kargatzea onartzen.\nErabili hornitu zaizun kargagailua soilik."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ez da USB bidez kargatzea onartzen."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Gailu honetan saioa hasita duen erabiltzaileak ezin du aktibatu USB arazketa. Eginbide hori erabiltzeko, aldatu erabiltzaile nagusira."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Handiagotu pantaila betetzeko"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Luzatu pantaila betetzeko"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Pantaila-argazkia"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Pantaila-argazkia gordetzen…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Pantaila-argazkia gordetzen…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Pantaila-argazkia gordetzen ari da."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Pantaila-argazkia atera da."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Sakatu pantaila-argazkia ikusteko."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Ezin izan da pantaila-argazkia atera."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Arazo bat izan da pantaila-argazkia gordetzean."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ezin da atera pantaila-argazkia ez delako tokirik geratzen."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Pantaila-argazkia gordetzen ari da"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Gorde da pantaila-argazkia"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Sakatu pantaila-argazkia ikusteko"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ezin izan da atera pantaila-argazkia"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Arazo bat izan da pantaila-argazkia gordetzean"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Ezin da atera pantaila-argazkia ez delako gelditzen tokirik"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB fitxategiak transferitzeko aukerak"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Muntatu multimedia-erreproduzigailu gisa (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desaktibatuta"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Bateria-mailaren arabera jakinarazpenak kontrolatzeko aukerekin, 0 eta 5 bitarteko garrantzi-mailetan sailka ditzakezu aplikazioen jakinarazpenak. \n\n"<b>"5. maila"</b>" \n- Erakutsi jakinarazpenen zerrendaren goialdean. \n- Baimendu etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"4. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Agerrarazi beti jakinarazpenak. \n\n"<b>"3. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n\n"<b>"2. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n\n"<b>"1. maila"</b>" \n- Galarazi etetea pantaila osoko moduan zaudenean. \n- Ez agerrarazi jakinarazpenik inoiz. \n- Ez egin soinurik edo dardararik inoiz. \n- Ezkutatu pantaila blokeatutik eta egoera-barratik. \n- Erakutsi jakinarazpenen zerrendaren behealdean. \n\n"<b>"0. maila"</b>" \n- Blokeatu aplikazioaren jakinarazpen guztiak."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Jakinarazpenak"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Aurrerantzean ez duzu jasoko horrelako jakinarazpenik"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Jakinarazpenen <xliff:g id="NUMBER">%d</xliff:g> kategoria"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Aplikazio honek ez du jakinarazpen-kategoriarik"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Ezin dira desaktibatu aplikazio honen jakinarazpenak"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Aplikazio honen 1/<xliff:g id="NUMBER_1">%s</xliff:g> jakinarazpen-kategoria</item> - <item quantity="one">Aplikazio honen 1/<xliff:g id="NUMBER_0">%s</xliff:g> jakinarazpen-kategoria</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> eta beste <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> eta beste <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Aurrerantzean ez duzu ikusiko horrelako jakinarazpenik"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Jakinarazpenak erakusten jarraitzea nahi duzu?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Blokeatu jakinarazpenak"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Jarraitu erakusten"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Aplikazio honen jakinarazpenak erakusten jarraitzea nahi duzu?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Jakinarazpen hauek ezin dira desaktibatu"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Ireki dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Itxi dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Onartu kanal honen jakinarazpenak"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Kategoria guztiak"</string> <string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Pertsonalizatu: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Pertsonalizatu"</string> <string name="notification_done" msgid="5279426047273930175">"Eginda"</string> + <string name="inline_undo" msgid="558916737624706010">"Desegin"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"jakinarazpena kontrolatzeko aukerak"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"jakinarazpena atzeratzeko aukerak"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Zabaldu"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizatu"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Itxi"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Ezarpenak"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Baztertzeko, arrastatu behera"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menua"</string> <string name="pip_notification_title" msgid="3204024940158161322">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string> @@ -763,7 +759,7 @@ <string name="tuner_right" msgid="6222734772467850156">"Eskuinera"</string> <string name="tuner_menu" msgid="191640047241552081">"Menua"</string> <string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g> aplikazioa"</string> - <string name="notification_channel_alerts" msgid="4496839309318519037">"Abisuak"</string> + <string name="notification_channel_alerts" msgid="4496839309318519037">"Alertak"</string> <string name="notification_channel_battery" msgid="5786118169182888462">"Bateria"</string> <string name="notification_channel_screenshot" msgid="6314080179230000938">"Pantaila-argazkiak"</string> <string name="notification_channel_general" msgid="4525309436693914482">"Mezu orokorrak"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 834397d8c702..e5975696614d 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"در حال انجام"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"اعلانها"</string> <string name="battery_low_title" msgid="6456385927409742437">"شارژ باتری کم است"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"شارژ باتری کم است. «بهینهسازی باتری» را روشن کنید"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده، براساس میزان مصرف شما حدود <xliff:g id="TIME">%s</xliff:g> باقی مانده است"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده، حدود <xliff:g id="TIME">%s</xliff:g> باقی مانده است"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی مانده است. بهینهسازی باتری روشن است."</string> <string name="invalid_charger" msgid="4549105996740522523">"شارژ USB پشتیبانی نمیشود.\nفقط از شارژر ارائه شده استفاده کنید."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"شارژ با USB پشتیبانی نمیشود."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"کاربری که درحال حاضر در این دستگاه وارد سیستم شده است نمیتواند اشکالزدایی USB را روشن کند. برای استفاده از این قابلیت، به کاربر اصلی تغییر وضعیت دهید."</string> <string name="compat_mode_on" msgid="6623839244840638213">"بزرگنمایی برای پر کردن صفحه"</string> <string name="compat_mode_off" msgid="4434467572461327898">"گسترده کردن برای پر کردن صفحه"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"عکس صفحهنمایش"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"در حال ذخیره عکس صفحهنمایش..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"در حال ذخیره عکس صفحهنمایش..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"عکس صفحهنمایش ذخیره شد."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"عکس صفحهنمایش گرفته شد."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"برای مشاهده عکس صفحهنمایشتان ضربه بزنید."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"عکس صفحهنمایش گرفته نشد."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"هنگام ذخیره عکس صفحهنمایش مشکلی رخ داد."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"به دلیل محدود بودن فضای ذخیرهسازی نمیتوانید عکس صفحهنمایش را ذخیره کنید."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"عکس صفحهنمایش درحال ذخیره شدن است"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"عکس صفحهنمایش ذخیره شد"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"برای مشاهده عکس صفحهنمایشتان ضربه بزنید"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"عکس صفحهنمایش گرفته نشد"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"هنگام ذخیره کردن عکس صفحهنمایش مشکلی پیش آمد"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"به دلیل محدود بودن فضای ذخیرهسازی نمیتوان عکس صفحهنمایش را ذخیره کرد"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"برنامه یا سازمان شما اجازه نمیدهند عکس صفحهنمایش بگیرید."</string> <string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"خاموش"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"با کنترلهای قدرتمند اعلان میتوانید سطح اهمیت اعلانهای هر برنامه را از ۰ تا ۵ تعیین کنید. \n\n"<b>"سطح ۵"</b>" \n- در صدر فهرست اعلانها نشان داده میشود \n- وقفه برای نمایش تمامصفحه مجاز است \n- همیشه اجمالی نشان داده میشود \n\n"<b>"سطح ۴"</b>" \n- وقفه برای نمایش تمامصفحه مجاز نیست \n- همیشه اجمالی نشان داده میشود \n\n"<b>"سطح ۳"</b>" \n- وقفه برای نمایش تمامصفحه مجاز نیست \n- هیچوقت اجمالی نشان داده نمیشود \n\n"<b>"سطح ۲"</b>" \n- وقفه برای نمایش تمامصفحه مجاز نیست \n- هیچوقت اجمالی نشان داده نمیشود \n- هیچوقت صدا و لرزش ایجاد نمیکند \n\n"<b>"سطح ۱"</b>" \n- نمایش تمام صفحه مجاز نیست \n- هیچوقت اجمالی نشان داده نمیشود \n- هیچوقت صدا یا لرزش ایجاد نمیکند \n- در قفل صفحه و نوار وضعیت پنهان است \n- در پایین فهرست اعلانها نشان داده میشود \n\n"<b>"سطح ۰"</b>" \n- همه اعلانهای این برنامه مسدود است"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"اعلانها"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"دیگر این اعلانها را دریافت نخواهید کرد"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> دسته اعلان"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"این برنامه دسته اعلان ندارد"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"نمیتوان اعلانهای این برنامه را خاموش کرد"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">۱ از <xliff:g id="NUMBER_1">%s</xliff:g> دسته اعلان این برنامه</item> - <item quantity="other">۱ از <xliff:g id="NUMBER_1">%s</xliff:g> دسته اعلان این برنامه</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و <xliff:g id="NUMBER_5">%3$d</xliff:g> مورد دیگر</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> و <xliff:g id="NUMBER_5">%3$d</xliff:g> مورد دیگر</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"دیگر این اعلانها را نخواهید دید"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"نمایش این اعلانها ادامه یابد؟"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"توقف اعلانها"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"همچنان نشان داده شود"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"نمایش اعلان از این برنامه ادامه یابد؟"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"نمیتوان این اعلانها را خاموش کرد"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"مجاز کردن اعلانهای این کانال"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"همه دستهها"</string> <string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"سفارشی کردن: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"سفارشی کردن"</string> <string name="notification_done" msgid="5279426047273930175">"تمام"</string> + <string name="inline_undo" msgid="558916737624706010">"واگرد"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"کنترلهای اعلان"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"گزینههای تعویق اعلان"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 51a71dedde22..59327c6107b9 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Käynnissä olevat"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ilmoitukset"</string> <string name="battery_low_title" msgid="6456385927409742437">"Akku on vähissä"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akku on vähissä, ota virransäästö käyttöön"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> jäljellä"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> käytettävissä, noin <xliff:g id="TIME">%s</xliff:g> jäljellä käytön perusteella"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> käytettävissä, noin <xliff:g id="TIME">%s</xliff:g> jäljellä"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> jäljellä. Virransäästö on käytössä."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-latausta ei tueta.\nKäytä laitteen mukana tullutta laturia."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB-latausta ei tueta."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Laitteelle tällä hetkellä kirjautunut käyttäjä ei voi ottaa USB-vianetsintää käyttöön. Vaihda käyttäjäksi ensisijainen käyttäjä, jotta voit käyttää tätä ominaisuutta."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoomaa koko näyttöön"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Venytä koko näyttöön"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Kuvakaappaus"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Tallennetaan kuvakaappausta..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Tallennetaan kuvakaappausta..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Kuvakaappausta tallennetaan."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvakaappaus tallennettu"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tarkastele kuvakaappausta napauttamalla"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Kuvakaappausta ei voitu tallentaa"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Kuvakaappausta tallennettaessa tapahtui virhe."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kuvakaappauksen tallentaminen epäonnistui, sillä tallennustilaa ei ole riittävästi."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Kuvakaappausta tallennetaan"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Kuvakaappaus tallennettu"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Napauta katsoaksesi kuvakaappausta"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Kuvakaappauksen tallennus epäonnistui"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Kuvakaappausta tallennettaessa tapahtui virhe"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB-tiedostonsiirtoasetukset"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Käytä mediasoittimena (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Pois käytöstä"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Ilmoitusten tehohallinnan avulla voit määrittää sovelluksen ilmoituksille tärkeystason väliltä 0–5. \n\n"<b>"Taso 5"</b>" \n– Ilmoitukset näytetään ilmoitusluettelon yläosassa \n– Näkyminen koko näytön tilassa sallitaan \n– Ilmoitukset kurkistavat aina näytölle\n\n"<b>"Taso 4"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ilmoitukset kurkistavat aina näytölle \n\n"<b>"Taso 3"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n\n"<b>"Taso 2"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n\n"<b>"Taso 1"</b>" \n– Näkyminen koko näytön tilassa estetään \n– Ei kurkistamista \n– Ei ääniä eikä värinää \n– Ilmoitukset piilotetaan lukitusnäytöltä ja tilapalkista \n– Ilmoitukset näytetään ilmoitusluettelon alaosassa \n\n"<b>"Taso 0"</b>" \n– Kaikki sovelluksen ilmoitukset estetään"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Ilmoitukset"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Et saa näitä ilmoituksia enää."</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ilmoitusluokkaa"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Tällä sovelluksella ei ole ilmoitusluokkia."</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Tämän sovelluksen ilmoituksia ei voi poistaa käytöstä."</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Tämä sovellus: 1/<xliff:g id="NUMBER_1">%s</xliff:g> ilmoitusluokkaa</item> - <item quantity="one">Tämä sovellus: 1/<xliff:g id="NUMBER_0">%s</xliff:g> ilmoitusluokkaa</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ja <xliff:g id="NUMBER_5">%3$d</xliff:g> muuta</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> ja <xliff:g id="NUMBER_2">%3$d</xliff:g> toinen</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Et näe näitä ilmoituksia enää"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Jatketaanko näiden ilmoitusten näyttämistä?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Lopeta ilmoitukset"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Jatka näyttämistä"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Jatketaanko ilmoitusten näyttämistä tästä sovelluksesta?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Näitä ilmoituksia ei voi poistaa käytöstä"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on avattu."</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on suljettu."</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Salli ilmoitukset tältä kanavalta."</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Kaikki luokat"</string> <string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Muokkaa: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Muokkaa"</string> <string name="notification_done" msgid="5279426047273930175">"Valmis"</string> + <string name="inline_undo" msgid="558916737624706010">"Kumoa"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"Ilmoitusten hallinta"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Ilmoitusten torkkuasetukset"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Laajenna"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Pienennä"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Sulje"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Asetukset"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Hylkää vetämällä alas."</string> <string name="pip_menu_title" msgid="4707292089961887657">"Valikko"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 1ddc394d9e01..16a4eac8af1d 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Pile faible"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"La pile est faible. Activez le mode Économie d\'énergie."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Il reste <xliff:g id="PERCENTAGE">%s</xliff:g>, environ <xliff:g id="TIME">%s</xliff:g> en fonction de votre usage"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Il reste <xliff:g id="PERCENTAGE">%s</xliff:g>, environ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants. La fonction Économie d\'énergie est activée."</string> <string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non compatible.\nVous devez utiliser le chargeur fourni."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Le chargement par USB n\'est pas pris en charge."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Enregistrement capture écran…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement capture écran…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Enregistrement de la capture d\'écran en cours…"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Touchez pour afficher votre capture d\'écran."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Une erreur s\'est produite lors de l\'enregistrement de la saisie d\'écran."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la saisie d\'écran, car l\'espace de stockage est limité."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Enregistrement de la capture d\'écran en cours…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Capture d\'écran enregistrée"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Touchez pour afficher votre capture d\'écran"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossible de réaliser une capture d\'écran"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Une erreur s\'est produite lors de l\'enregistrement de la capture d\'écran"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Installer comme un lecteur multimédia (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Désactivé"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Avec les réglages avancés des notifications, vous pouvez définir un degré d\'importance de 0 à 5 pour les notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher dans le haut de la liste des notifications \n- Autoriser les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 4"</b>" \n- Empêcher les interruptions en mode plein écran \n- Toujours afficher les aperçus \n\n"<b>"Niveau 3"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n\n"<b>"Niveau 2"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n\n"<b>"Niveau 1"</b>" \n- Empêcher les interruptions en mode plein écran \n- Ne jamais afficher les aperçus \n- Ne pas autoriser les sons et les vibrations \n- Masquer de l\'écran de verrouillage et de la barre d\'état status bar \n- Afficher dans le bas de la liste des notifications \n\n"<b>"Level 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Vous ne recevrez plus ces notifications"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> catégories de notification"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Cette application n\'a pas de catégories de notification"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Les notifications de cette application ne peuvent pas être désactivées"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item> - <item quantity="other">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autre</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autres</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne verrez plus ces notifications"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Continuer à afficher ces notifications?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuez à afficher"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont fermés"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Autoriser les notifications de cette chaîne"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Toutes les catégories"</string> <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personnaliser : <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personnaliser"</string> <string name="notification_done" msgid="5279426047273930175">"Terminé"</string> + <string name="inline_undo" msgid="558916737624706010">"Annuler"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"paramètres des notifications"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"options de répétition des notifications"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Réduire"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Fermer"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Paramètres"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Faire glisser vers le bas pour ignorer"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index f6e98a37fb37..cc8bcbab4e15 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batterie faible"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batterie faible : activez l\'économiseur de batterie"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant : environ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants. L\'économiseur de batterie est activé."</string> <string name="invalid_charger" msgid="4549105996740522523">"Chargement USB non disponible.\nVous devez utiliser le chargeur fourni."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Chargeur USB non compatible."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Enregistrement capture écran…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement de la capture d\'écran…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Enregistrement de la capture d\'écran en cours…"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Appuyez pour afficher votre capture d\'écran."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Erreur lors de l\'enregistrement de la capture d\'écran."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Enregistrement de la capture d\'écran…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Capture d\'écran enregistrée"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Appuyez pour afficher votre capture d\'écran"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossible d\'effectuer une capture d\'écran"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Erreur lors de l\'enregistrement de la capture d\'écran"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Installer en tant que lecteur multimédia (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Désactivé"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Grâce aux commandes de gestion des notifications, vous pouvez définir le niveau d\'importance (compris entre 0 et 5) des notifications d\'une application. \n\n"<b>"Niveau 5"</b>" \n- Afficher en haut de la liste des notifications \n- Autoriser l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 4"</b>" \n- Empêcher l\'interruption en plein écran \n- Toujours en aperçu \n\n"<b>"Niveau 3"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n\n"<b>"Niveau 2"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n\n"<b>"Niveau 1"</b>" \n- Empêcher l\'interruption en plein écran \n- Jamais en aperçu \n- Ne jamais émettre de signal sonore ni déclencher le vibreur \n- Masquer les notifications dans l\'écran de verrouillage et la barre d\'état \n- Afficher au bas de la liste des notifications \n\n"<b>"Niveau 0"</b>" \n- Bloquer toutes les notifications de l\'application"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifications"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Vous ne recevrez plus ces notifications"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> catégories de notification"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Cette application n\'a pas de catégories de notification"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Impossible de désactiver les notifications de cette application"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item> - <item quantity="other">1 catégorie de notification sur <xliff:g id="NUMBER_1">%s</xliff:g> provenant de cette application</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autre</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> et <xliff:g id="NUMBER_5">%3$d</xliff:g> autres</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Vous ne recevrez plus ces notifications"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Continuer d\'afficher ces notifications ?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Arrêter les notifications"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuer d\'afficher les notifications"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer d\'afficher les notifications de cette application ?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ces notifications ne peuvent pas être désactivées"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Les commandes de notification sont disponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Les commandes de notification sont indisponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Autoriser les notifications pour cette chaîne"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Toutes les catégories"</string> <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personnaliser : <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personnaliser"</string> <string name="notification_done" msgid="5279426047273930175">"Terminé"</string> + <string name="inline_undo" msgid="558916737624706010">"Annuler"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> : <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"paramètres des notifications"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"options de répétition des notifications"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Développer"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Réduire"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Fermer"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Paramètres"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Faire glisser vers le bas pour ignorer"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 987ea598d079..23a22e63defc 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En curso"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificacións"</string> <string name="battery_low_title" msgid="6456385927409742437">"Queda pouca batería"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Queda pouca batería. Activa a función Aforro de batería"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>, é dicir, aproximadamente <xliff:g id="TIME">%s</xliff:g> en función do uso que fas"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g>, é dicir, aproximadamente <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Está activada a función Aforro de batería."</string> <string name="invalid_charger" msgid="4549105996740522523">"Non compatible coa carga por USB.\nUtiliza só o cargador proporcionado."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Non se admite a carga mediante USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuario coa sesión iniciada actualmente neste dispositivo non pode activar a depuración por USB. Para utilizar esta función, cambia ao usuario principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Ampliar ata ocupar todo"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Estirar ata ocupar todo"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Gardando captura de pantalla…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Gardando captura de pantalla…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Estase gardando a captura de pantalla."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de pantalla gardada."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toca para ver a captura de pantalla."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Non se puido facer a captura de pantalla."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Produciuse un problema ao gardar a captura de pantalla."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Non se pode gardar a captura de pantalla porque o espazo de almacenamento é limitado."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Estase gardando a captura de pantalla"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Gardouse a captura de pantalla"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toca para ver a captura de pantalla"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Non se puido facer a captura de pantalla"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Produciuse un problema ao gardar a captura de pantalla"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opcións de transferencia USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Inserir como reprodutor multimedia (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desactivar"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Cos controis de notificacións mellorados, podes asignarlles un nivel de importancia comprendido entre 0 e 5 ás notificacións dunha aplicación determinada. \n\n"<b>"Nivel 5"</b>" \n- Mostrar na parte superior da lista de notificacións. \n- Permitir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 4"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Mostrar sempre. \n\n"<b>"Nivel 3"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n\n"<b>"Nivel 2"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n\n"<b>"Nivel 1"</b>" \n- Impedir interrupcións no modo de pantalla completa. \n- Non mostrar nunca. \n- Non soar nin vibrar nunca. \n- Ocultar na pantalla de bloqueo e na barra de estado. \n- Mostrar na parte inferior da lista de notificacións. \n\n"<b>"Nivel 0"</b>" \n- Bloquear todas as notificacións da aplicación."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificacións"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Deixarás de recibir estas notificacións"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorías de notificacións"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicación non ten categorías de notificacións"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Non se poden desactivar as notificacións desta aplicación"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorías de notificacións desta aplicación</item> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoría de notificación desta aplicación</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> máis</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e <xliff:g id="NUMBER_2">%3$d</xliff:g> máis</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Deixarás de ver estas notificacións"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Queres seguir mostrando estas notificacións?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Deter notificacións"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando notificacións"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Queres seguir mostrando as notificacións desta aplicación?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Non se poden desactivar estas notificacións"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Abríronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Pecháronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificacións desde esta canle"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorías"</string> <string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Feito"</string> + <string name="inline_undo" msgid="558916737624706010">"Desfacer"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controis de notificacións"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcións para adiar notificacións"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Despregar"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Pechar"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Configuración"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastra cara abaixo para ignorar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menú"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index c024531a16ee..22966a7a2923 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ચાલુ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"નોટિફિકેશનો"</string> <string name="battery_low_title" msgid="6456385927409742437">"બૅટરી ઓછી છે"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી. બૅટરી સેવર ચાલુ છે."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ચાર્જિંગ સમર્થિત નથી.\nફક્ત આપવામાં આવેલ ચાર્જરનો ઉપયોગ કરો."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ચાર્જિંગ સમર્થિત નથી."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"હાલમાં આ ઉપકરણમાં સાઇન ઇન થયેલ વપરાશકર્તા USB ડિબગીંગ ચાલુ કરી શકતા નથી. આ સુવિધાનો ઉપયોગ કરવા માટે પ્રાથમિક વપરાશકર્તા પર સ્વિચ કરો."</string> <string name="compat_mode_on" msgid="6623839244840638213">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string> <string name="compat_mode_off" msgid="4434467572461327898">"સ્ક્રીન ભરવા માટે ખેંચો"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"સ્ક્રીનશોટ સાચવવામાં આવી રહ્યો છે."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"સ્ક્રીનશોટ કેપ્ચર કર્યો."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"તમારા સ્ક્રીનશૉટને જોવા માટે ટૅપ કરો."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"સ્ક્રીનશોટ કેપ્ચર કરી શકાયો નથી."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"સ્ક્રીનશૉટ સાચવવામાં સમસ્યા આવી."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"મર્યાદિત સંગ્રહ સ્થાનને કારણે સ્ક્રીનશોટ સાચવી શકાતો નથી."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ફાઇલ ટ્રાન્સફર વિકલ્પો"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"મીડિયા પ્લેયર તરીકે માઉન્ટ કરો (MTP)"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"બંધ"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"પાવર સૂચના નિયંત્રણો સાથે, તમે ઍપની સૂચનાઓ માટે 0 થી 5 સુધીના મહત્વના સ્તરને સેટ કરી શકો છો. \n\n"<b>"સ્તર 5"</b>" \n- સૂચના સૂચિની ટોચ પર બતાવો \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 4"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- હંમેશાં ત્વરિત દૃષ્ટિ કરો \n\n"<b>"સ્તર 3"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n\n"<b>"સ્તર 2"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધ અટકાવો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n\n"<b>"સ્તર 1"</b>" \n- પૂર્ણ સ્ક્રીન અવરોધની મંજૂરી આપો \n- ક્યારેય ત્વરિત દૃષ્ટિ કરશો નહીં \n- ક્યારેય અવાજ અથવા વાઇબ્રેટ કરશો નહીં \n- લૉક સ્ક્રીન અને સ્ટેટસ બારથી છુપાવો \n- સૂચના સૂચિના તળિયા પર બતાવો \n\n"<b>"સ્તર 0"</b>" \n- ઍપની તમામ સૂચનાઓને બ્લૉક કરો"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"નોટિફિકેશનો"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"તમને હવે આ સૂચનાઓ મળશે નહીં"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> સૂચના કૅટેગરીઓ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"આ ઍપ્લિકેશનમાં સૂચના કૅટેગરી નથી"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"આ ઍપ્લિકેશનની સૂચનાઓ બંધ કરી શકાતી નથી"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">આ ઍપ્લિકેશનમાંની <xliff:g id="NUMBER_1">%s</xliff:g> સૂચના કૅટેગરીમાંથી 1</item> - <item quantity="other">આ ઍપ્લિકેશનમાંની <xliff:g id="NUMBER_1">%s</xliff:g> સૂચના કૅટેગરીમાંથી 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> અને અન્ય <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> અને અન્ય <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો ચાલુ છે"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો બંધ છે"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"આ ચૅનલની સૂચનાઓને મંજૂરી આપો"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"બધી કૅટેગરી"</string> <string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"કસ્ટમાઇઝ કરો: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"સૂચના નિયંત્રણો"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"સૂચના સ્નૂઝ કરવાના વિકલ્પો"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"વિસ્તૃત કરો"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"નાનું કરો"</string> <string name="pip_phone_close" msgid="8416647892889710330">"બંધ કરો"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"સેટિંગ"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"છોડી દેવા માટે નીચે ખેંચો"</string> <string name="pip_menu_title" msgid="4707292089961887657">"મેનૂ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ચિત્રમાં-ચિત્રની અંદર છે"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index f5f8c7c607d3..ec3785589ade 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ऑनगोइंग"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाएं"</string> <string name="battery_low_title" msgid="6456385927409742437">"बैटरी कम है"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"बैटरी कम बची है. बैटरी सेवर चालू करें"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शेष"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> बची है, आपके इस्तेमाल करने के तरीके के हिसाब से बैटरी लगभग <xliff:g id="TIME">%s</xliff:g> चलेगी"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> बची है, बैटरी लगभग <xliff:g id="TIME">%s</xliff:g> चलेगी"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है. बैटरी सेवर चालू है."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB चार्जिंग समर्थित नहीं है.\nकेवल आपूर्ति किए गए चार्जर का उपयोग करें."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्जिंग समर्थित नहीं है."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"अभी इस डिवाइस में जिस उपयोगकर्ता ने साइन इन किया है, वो USB डीबगिंग चालू नहीं कर सकता. इस सुविधा का इस्तेमाल करने के लिए, प्राथमिक उपयोगकर्ता में बदलें."</string> <string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरने के लिए ज़ूम करें"</string> <string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरने के लिए खींचें"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"स्क्रीनशॉट"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रीनशॉट सहेजा जा रहा है..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रीनशॉट सहेजा जा रहा है..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रीनशॉट सहेजा जा रहा है."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कैप्चर किया गया."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"अपना स्क्रीनशॉट देखने के लिए टैप करें."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट को कैप्चर नहीं किया जा सका."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रीनशॉट सहेजने में समस्या आई"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"मेमोरी में जगह कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सकता."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"स्क्रीनशॉट सेव किया जा रहा है"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"स्क्रीनशॉट सेव किया गया"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"अपना स्क्रीनशॉट देखने के लिए टैप करें"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"स्क्रीनशॉट नहीं लिया जा सका"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"स्क्रीनशॉट सेव करते समय एक समस्या आई"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB फ़ाइल स्थानांतरण विकल्प"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेयर के रूप में माउंट करें (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"पावर सूचना नियंत्रण के ज़रिये, आप किसी ऐप की सूचना को उसकी अहमियत के हिसाब से 0 से 5 के लेवल पर सेट कर सकते हैं.\n\n"<b>"लेवल 5"</b>" \n- सूचना सूची में सबसे ऊपर दिखाएं \n- पूरे स्क्रीन को ढंकने की अनुमति दें \n- लगातार देखते रहें \n\n"<b>" लेवल 4"</b>" \n- पूरे स्क्रीन को ढंकें \n- लगातार देखते रहें \n\n"<b>"लेवल 3"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n-कभी भी न देखें \n\n"<b>"लेवल 2"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n\n"<b>"लेवल 1"</b>" \n- पूरे स्क्रीन को ढंकने से रोकें \n- कभी भी न देखें \n- कभी भी आवाज़ या कंपन (वाइब्रेशन) न करें \n- लॉक स्क्रीन और स्टेटस बार से छिपाएं \n- सूचना सूची के नीचे दिखाएं \n\n"<b>"लेवल 0"</b>" \n- ऐप्लिकेशन की सभी सूचनाएं रोक दें"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"अब आपको ये सूचनाएं नहीं मिलेंगी"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"सूचना की <xliff:g id="NUMBER">%d</xliff:g> श्रेणियां"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"इस ऐप्लिकेशन में सूचना श्रेणियां नहीं हैं"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"इस ऐप की सूचनाएं बंद नहीं की जा सकती"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">इस ऐप की <xliff:g id="NUMBER_1">%s</xliff:g> सूचना श्रेणियों में से 1 श्रेणी</item> - <item quantity="other">इस ऐप की <xliff:g id="NUMBER_1">%s</xliff:g> सूचना श्रेणियों में से 1 श्रेणी</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> और <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> और <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"अब आपको ये सूचनाएं दिखाई नहीं देंगी"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"ये सूचनाएं दिखाना जारी रखें?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"सूचनाएं दिखाना बंद करें"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"दिखाना जारी रखें"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ये सूचनाएं दिखाया जाना बंद नहीं किया जा सकता"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण बंद हैं"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"इस चैनल से सूचना की पाने की मंज़ूरी दें"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"सभी श्रेणियां"</string> <string name="notification_more_settings" msgid="816306283396553571">"और सेटिंग"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"अपनी पसंद के मुताबिक बनाएं: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"पसंद के मुताबिक बनाएं"</string> <string name="notification_done" msgid="5279426047273930175">"हो गया"</string> + <string name="inline_undo" msgid="558916737624706010">"पहले जैसा करें"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना नियंत्रण"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना को स्नूज़ (थोड़ी देर के लिए चुप करना) करने के विकल्प"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तार करें"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"छोटा करें"</string> <string name="pip_phone_close" msgid="8416647892889710330">"बंद करें"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिंग"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"खारिज करने के लिए नीचे खींचें और छोड़ें"</string> <string name="pip_menu_title" msgid="4707292089961887657">"मेन्यू"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> पिक्चर में पिक्चर के अंदर है"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 7395b6605217..cb6283478cec 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"U tijeku"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obavijesti"</string> <string name="battery_low_title" msgid="6456385927409742437">"Niska razina baterije"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterija je skoro prazna. Uključite Štednju baterije"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Preostalo <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g>, još otprilike <xliff:g id="TIME">%s</xliff:g> na temelju vaše upotrebe"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g>, još otprilike <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Preostalo <xliff:g id="PERCENTAGE">%s</xliff:g>. Uključena je Štednja baterije."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB punjenje nije podržano.\nUpotrijebite samo priloženi punjač."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Punjenje putem USB-a nije podržano."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutačno prijavljen na ovaj uređaj ne može uključiti otklanjanje pogrešaka putem USB-a. Da biste upotrebljavali tu značajku, prijeđite na primarnog korisnika."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj i ispuni zaslon"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Rastegni i ispuni zaslon"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snimka zaslona"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Spremanje snimke zaslona..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Spremanje snimke zaslona..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Spremanje snimke zaslona."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Zaslon je snimljen."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Dodirnite da biste vidjeli snimku zaslona."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nije bilo moguće snimiti zaslon."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Prilikom spremanja snimke zaslona pojavio se problem."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Zaslon nije snimljen zbog ograničenog prostora za pohranu."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Spremanje snimke zaslona"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Snimka zaslona spremljena"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Dodirnite da biste vidjeli snimku zaslona"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Nije bilo moguće snimiti zaslon"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Prilikom spremanja snimke zaslona pojavio se problem"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prijenosa datoteka"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Učitaj kao media player (MTP)"</string> @@ -559,28 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Isključeno"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Napredne kontrole obavijesti omogućuju vam da postavite razinu važnosti za obavijesti aplikacije od 0 do 5. \n\n"<b>"Razina 5"</b>" \n– prikaži na vrhu popisa obavijesti \n– dopusti prekide prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 4"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– uvijek dopusti brzi pregled \n\n"<b>"Razina 3"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled\n\n"<b>"Razina 2"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n\n"<b>"Razina 1"</b>" \n– onemogući prekid prikaza na cijelom zaslonu \n– nikad ne dopusti brzi pregled \n– nikad ne emitiraj zvuk ni vibraciju \n– ne prikazuj na zaključanom zaslonu i traci statusa \n– prikaži na dnu popisa obavijesti \n\n"<b>"Razina 0"</b>" \n– blokiraj sve obavijesti aplikacije"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Obavijesti"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Više nećete primati te obavijesti"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Broj kategorija obavijesti: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ova aplikacija nema kategorije obavijesti"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Obavijesti ove aplikacije ne mogu se isključiti"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obavijesti iz ove aplikacije</item> - <item quantity="few">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obavijesti iz ove aplikacije</item> - <item quantity="other">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorija obavijesti iz ove aplikacije</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i još <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Te vam se obavijesti više neće prikazivati"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Želite li da se obavijesti nastave prikazivati?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Zaustavi obavijesti"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivati"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obavijesti te aplikacije nastave prikazivati?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Te se obavijesti ne mogu isključiti"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Otvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Zatvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Dopusti obavijesti za ovaj kanal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Sve kategorije"</string> <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Prilagodite: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Prilagodi"</string> <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string> + <string name="inline_undo" msgid="558916737624706010">"Poništi"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrole obavijesti"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcije odgode obavijesti"</string> @@ -737,8 +732,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Proširivanje"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimiziraj"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Postavke"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povucite prema dolje da biste odbacili"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Izbornik"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 87ca49a18a83..2069952cde5c 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Folyamatban van"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Értesítések"</string> <string name="battery_low_title" msgid="6456385927409742437">"Alacsony az energiaszint"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Az akkumulátor szintje alacsony. Kapcsolja be az akkumulátorkímélő módot."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> maradt"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> maradt, körülbelül <xliff:g id="TIME">%s</xliff:g> van hátra a használat alapján"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> maradt, körülbelül <xliff:g id="TIME">%s</xliff:g> van hátra"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> maradt. Az Akkumulátorkímélő mód be van kapcsolva."</string> <string name="invalid_charger" msgid="4549105996740522523">"Az USB-n keresztüli töltés nincs támogatva.\nHasználja a kapott töltőt."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Az USB-n keresztüli töltés nem támogatott."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Az eszközre jelenleg bejelentkezett felhasználó nem engedélyezheti az USB-hibakeresést. A funkció használatához váltson az elsődleges felhasználóra."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Nagyítás a kitöltéshez"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Nyújtás kitöltéshez"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Képernyőkép"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Képernyőkép mentése..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Képernyőkép mentése..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Képernyőkép mentése."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Képernyőkép rögzítve."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Koppintson a képernyőkép megtekintéséhez."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nem sikerült rögzíteni a képernyőképet."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hiba történt a képernyőkép mentése során."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nem menthet képernyőképet, mert kevés a tárhely."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Képernyőkép mentése…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"A képernyőkép mentése sikerült"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Koppintson a képernyőkép megtekintéséhez"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Nem sikerült rögzíteni a képernyőképet"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Hiba történt a képernyőkép mentése során"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Nem menthet képernyőképet, mert kevés a tárhely"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB-fájlátvitel beállításai"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Csatlakoztatás médialejátszóként (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Kikapcsolva"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Az értesítési beállítások révén 0-tól 5-ig állíthatja be a fontossági szintet az alkalmazás értesítéseinél. \n\n"<b>"5. szint"</b>" \n– Megjelenítés az értesítési lista tetején \n– Teljes képernyő megszakításának engedélyezése \n– Mindig felugrik \n\n"<b>"4. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Mindig felugrik \n\n"<b>"3. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n\n"<b>"2. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés és rezgés \n\n"<b>"1. szint"</b>" \n– Teljes képernyő megszakításának megakadályozása \n– Soha nem ugrik fel \n– Soha nincs hangjelzés vagy rezgés \n– Elrejtés a lezárási képernyőről és az állapotsávról \n– Megjelenítés az értesítési lista alján \n\n"<b>"0. szint"</b>" \n– Az alkalmazás összes értesítésének letiltása"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Értesítések"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Többé nem jelennek meg ezek az értesítések"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> értesítéskategória"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Az alkalmazás nem rendelkezik értesítési kategóriákkal"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Az alkalmazástól érkező értesítések nem kapcsolhatók ki"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g>/1 értesítési kategória az alkalmazásból</item> - <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g>/1 értesítési kategória az alkalmazásból</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> és <xliff:g id="NUMBER_5">%3$d</xliff:g> másik</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> és <xliff:g id="NUMBER_2">%3$d</xliff:g> másik</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Többé nem jelennek meg ezek az értesítések"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Továbbra is megjelenjenek ezek az értesítések?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Értesítések letiltása"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Megjelenítés továbbra is"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Továbbra is megjelenjenek az alkalmazás értesítései?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ezeket az értesítéseket nem lehet kikapcsolni"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői megnyitva"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői kikapcsolva"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Értesítések engedélyezése erről a csatornáról"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Minden kategória"</string> <string name="notification_more_settings" msgid="816306283396553571">"További beállítások"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Személyre szabás: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Személyre szabás"</string> <string name="notification_done" msgid="5279426047273930175">"Kész"</string> + <string name="inline_undo" msgid="558916737624706010">"Visszavonás"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> – <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"értesítésvezérlők"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"értesítések halasztási beállításai"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Kibontás"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Kis méret"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Bezárás"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Beállítások"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Elvetéshez húzza lefelé"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string> <string name="pip_notification_title" msgid="3204024940158161322">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index b6172c8e555c..0656f0c4e09a 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string> <string name="battery_low_title" msgid="6456385927409742437">"Մարտկոցի լիցքը սպառվում է"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Մարտկոցի լիցքը սպառվում է։ Միացրեք մարտկոցի տնտեսումը։"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>, մնացել է մոտ <xliff:g id="TIME">%s</xliff:g>՝ օգտագործման եղանակից կախված"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>, մնացել է մոտ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Մնացել է <xliff:g id="PERCENTAGE">%s</xliff:g>: Մարտկոցի տնտեսումը միացված է:"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB լիցքավորումը չի աջակցվում:\nՕգտվեք միայն գործող լիցքավորիչից:"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB լիցքավորումը չի աջակցվում:"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ընթացիկ հաշվի օգտատերը չի կարող միացնել USB վրիպազերծումը: Այս գործառույթը միացնելու համար մուտք գործեք հիմնական օգտատիրոջ հաշվով:"</string> <string name="compat_mode_on" msgid="6623839244840638213">"Խոշորացնել` էկրանը լցնելու համար"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ձգել` էկրանը լցնելու համար"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Էկրանի պատկեր"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Էկրանի պատկերը պահվում է…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Էկրանի պատկերը պահվում է..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Էկրանի պատկերը պահվում է:"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Էկրանի պատկերը լուսանկարվել է:"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Հպեք՝ էկրանի պատկերը տեսնելու համար:"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Չհաջողվեց լուսանկարել էկրանի պատկերը:"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Էկրանի պատկերը պահելիս խնդիր առաջացավ:"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Չհաջողվեց պահել էկրանի պատկերը սահմանափակ հիշողության պատճառով:"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Էկրանի պատկերը պահվում է"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Էկրանի պատկերը պահվեց"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Հպեք՝ էկրանի պատկերը տեսնելու համար"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Չհաջողվեց պահել էկրանի պատկերը"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Չհաջողվեց պահել էկրանի պատկերը"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Չհաջողվեց պահել էկրանի պատկերը անբավարար հիշողության պատճառով"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում էկրանի պատկերի ստացումը"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ֆայլերի փոխանցման ընտրանքներ"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Միացնել որպես մեդիա նվագարկիչ (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Անջատել"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Ծանուցումների ընդլայնված կառավարման օգնությամբ կարող եք յուրաքանչյուր հավելվածի ծանուցումների համար նշանակել կարևորության աստիճան՝ 0-5 սահմաններում: \n\n"<b>"5-րդ աստիճան"</b>" \n- Ցուցադրել ծանուցումների ցանկի վերևում \n- Թույլատրել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"4-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Միշտ ցուցադրել կարճ ծանուցումները \n\n"<b>"3-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n\n"<b>"2-րդ աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n\n"<b>"1-ին աստիճան"</b>" \n- Արգելել լիաէկրան ընդհատումները \n- Արգելել կարճ ծանուցումների ցուցադրումը \n- Անջատել ձայնը և թրթռումը \n- Չցուցադրել կողպէկրանում և կարգավիճակի գոտում \n- Ցուցադրել ծանուցումների ցանկի ներքևում \n\n"<b>"0-րդ աստիճան"</b>\n"- Արգելափակել հավելվածի բոլոր ծանուցումները"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Ծանուցումներ"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Այս ծանուցումներն այլևս չեք ստանա"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ծանուցման կատեգորիաներ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Այս հավելվածը ծանուցման կատեգորիաներ չունի"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Այս հավելվածի ծանուցումները հնարավոր չէ անջատել"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 out of <xliff:g id="NUMBER_1">%s</xliff:g> notification categories from this app</item> - <item quantity="other">1 ալիք` այս հավելվածի <xliff:g id="NUMBER_1">%s</xliff:g> կատեգորիաներից</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, and <xliff:g id="NUMBER_5">%3$d</xliff:g> others</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ու <xliff:g id="NUMBER_5">%3$d</xliff:g> այլ</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Դուք այլևս չեք ստանա այս ծանուցումները"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Ցուցադրե՞լ այս ծանուցումները։"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Չցուցադրել ծանուցումներ"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Ցուցադրել"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Ցուցադրե՞լ ծանուցումներ այս հավելվածից։"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Այս ծանուցումները հնարավոր չէ անջատել"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը բաց է"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը փակ է"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Թույլ տալ ծանուցումներ այս ալիքից"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Բոլոր կատեգորիաները"</string> <string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Հարմարեցնել՝ <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Կարգավորել"</string> <string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string> + <string name="inline_undo" msgid="558916737624706010">"Հետարկել"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ծանուցման կառավարներ"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ծանուցման հետաձգման ընտրանքներ"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Ընդարձակել"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Ծալել"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Փակել"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Կարգավորումներ"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Քաշեք վար՝ փակելու համար"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Ընտրացանկ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>-ը «Նկար նկարի մեջ» ռեժիմում է"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 66ebb865ee6b..6d083089be7f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Berkelanjutan"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifikasi"</string> <string name="battery_low_title" msgid="6456385927409742437">"Baterai lemah"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Baterai hampir habis. Aktifkan Penghemat Baterai"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Sisa <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi berdasarkan penggunaan Anda"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Sisa <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>. Penghemat Baterai aktif."</string> <string name="invalid_charger" msgid="4549105996740522523">"Pengisian daya USB tidak didukung.\nGunakan hanya pengisi daya yang disediakan."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Pengisian daya USB tidak didukung."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Pengguna yang sedang login ke perangkat ini tidak dapat mengaktifkan proses debug USB. Beralihlah ke pengguna utama untuk menggunakan fitur ini."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Perbesar utk mengisi layar"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Rentangkn utk mngisi layar"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Menyimpan screenshot..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Menyimpan screenshot..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot sedang disimpan."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot diambil."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Ketuk untuk melihat screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat mengambil screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Terjadi masalah saat menyimpan screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot sedang disimpan"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot disimpan"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap untuk melihat screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Tidak dapat mengambil screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Terjadi masalah saat menyimpan screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opsi transfer file USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Pasang sebagai pemutar media (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Nonaktif"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Dengan kontrol notifikasi daya, Anda dapt menyetel level kepentingan notifikasi aplikasi dari 0 sampai 5. \n\n"<b>"Level 5"</b>" \n- Muncul di atas daftar notifikasi \n- Izinkan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 4"</b>" \n- Jangan interupsi layar penuh \n- Selalu intip pesan \n\n"<b>"Level 3"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n\n"<b>"Level 2"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara dan getaran \n\n"<b>"Level 1"</b>" \n- Jangan interupsi layar penuh \n- Tak pernah intip pesan \n- Tanpa suara atau getaran \n- Sembunyikan dari layar kunci dan bilah status \n- Muncul di bawah daftar notifikasi \n\n"<b>"Level 0"</b>" \n- Blokir semua notifikasi dari aplikasi"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifikasi"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Anda tidak akan mendapatkan notifikasi ini lagi"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> kategori notifikasi"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Aplikasi ini tidak memiliki kategori notifikasi"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notifikasi dari aplikasi ini tidak dapat dinonaktifkan"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 dari <xliff:g id="NUMBER_1">%s</xliff:g> kategori notifikasi dari aplikasi ini</item> - <item quantity="one">1 dari <xliff:g id="NUMBER_0">%s</xliff:g> kategori notifikasi dari aplikasi ini</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, dan <xliff:g id="NUMBER_5">%3$d</xliff:g> lainnya</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, dan <xliff:g id="NUMBER_2">%3$d</xliff:g> lainnya</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Anda tidak akan melihat notifikasi ini lagi"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Terus tampilkan notifikasi ini?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Hentikan notifikasi"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Terus tampilkan"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tampilkan notifikasi dari aplikasi ini?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Notifikasi ini tidak dapat dinonaktifkan"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ditutup"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Izinkan notifikasi dari saluran ini"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Semua Kategori"</string> <string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Sesuaikan: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Sesuaikan"</string> <string name="notification_done" msgid="5279426047273930175">"Selesai"</string> + <string name="inline_undo" msgid="558916737624706010">"Urungkan"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrol notifikasi"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opsi tunda notifikasi"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Luaskan"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalkan"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Tutup"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Setelan"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Seret ke bawah untuk menutup"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 35bf451d2500..dbdb2731f0cf 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Áframhaldandi"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Tilkynningar"</string> <string name="battery_low_title" msgid="6456385927409742437">"Rafhlaðan er að tæmast"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Lítil hleðsla er á rafhlöðunni. Kveiktu á rafhlöðusparnaði"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> eftir"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> eftir, um það bil <xliff:g id="TIME">%s</xliff:g> eftir miðað við notkun"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> eftir, um það bil <xliff:g id="TIME">%s</xliff:g> eftir"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> eftir. Kveikt er á rafhlöðusparnaði."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-hleðsla er ekki studd.\nNotaðu eingöngu hleðslutækið sem fylgdi."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ekki er stuðningur við USB-hleðslu."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Notandinn sem er skráður inn í þetta tæki núna getur ekki kveikt á USB-villuleit. Til þess að nota þennan eiginleika skaltu skipta yfir í aðalnotandann."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Fylla skjá með aðdrætti"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Teygja yfir allan skjáinn"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skjámynd"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Vistar skjámynd…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Vistar skjámynd…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Verið er að vista skjámynd."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skjámynd var tekin."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Ýttu til að sjá skjámyndina."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Ekki tókst að taka skjámynd."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Upp kom vandamál við að vista skjámynd."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Vistar skjámynd"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skjámynd vistuð"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Ýttu til skoða skjámyndina"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ekki tókst að taka skjámynd"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Vandamál kom upp við að vista skjámynd"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Valkostir USB-skráaflutnings"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Tengja sem efnisspilara (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Slökkt"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Með orkutilkynningastýringum geturðu stillt mikilvægi frá 0 upp í 5 fyrir tilkynningar forrita. \n\n"<b>"Stig 5"</b>" \n- Sýna efst á tilkynningalista \n- Leyfa truflun þegar birt er á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 4"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja alltaf \n\n"<b>"Stig 3"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n\n"<b>"Stig 2"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n\n"<b>"Stig 1"</b>" \n- Hindra truflun við birtingu á öllum skjánum \n- Kíkja aldrei \n- Slökkva á hljóði og titringi \n- Fela á lásskjá og stöðustiku \n- Sýna neðst á tilkynningalista \n\n"<b>"Stig 0"</b>" \n- Setja allar tilkynningar frá forriti á bannlista"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Tilkynningar"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Þú færð þessar tilkynningar ekki framar"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> tilkynningaflokkar"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Þetta forrit er ekki með tilkynningaflokka"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Ekki er hægt að slökkva á tilkynningum frá þessu forriti"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 af <xliff:g id="NUMBER_1">%s</xliff:g> tilkynningaflokki frá þessu forriti</item> - <item quantity="other">1 af <xliff:g id="NUMBER_1">%s</xliff:g> tilkynningaflokkum frá þessu forriti</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> í viðbót</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> í viðbót</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Þú munt ekki sjá þessar tilkynningar aftur"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Sýna áfram þessar tilkynningar?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stöðva tilkynningar"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Sýna áfram"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Sýna áfram tilkynningar frá þessu forriti?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ekki er hægt að slökkva á þessum tilkynningum"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Opnað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Lokað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Leyfa tilkynningar frá þessari rás"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Allir flokkar"</string> <string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Sérstilla: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Sérsníða"</string> <string name="notification_done" msgid="5279426047273930175">"Lokið"</string> + <string name="inline_undo" msgid="558916737624706010">"Afturkalla"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"tilkynningastýringar"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"þöggunarstillingar tilkynninga"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Stækka"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minnka"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Loka"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Stillingar"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Draga niður til að hunsa"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Valmynd"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index fec534d02e86..642a8dca063e 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batteria quasi scarica"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batteria quasi scarica. Attiva Risparmio energetico"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> rimanente"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME">%s</xliff:g> circa"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Tempo rimanente: <xliff:g id="TIME">%s</xliff:g> circa"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> rimanente. Risparmio energetico attivo."</string> <string name="invalid_charger" msgid="4549105996740522523">"Ricarica tramite USB non supportata.\nUtilizza solo il caricatore in dotazione."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ricarica tramite USB non supportata."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utente che ha eseguito l\'accesso a questo dispositivo non può attivare il debug USB. Per utilizzare questa funzione, passa all\'utente principale."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom per riempire schermo"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Estendi per riemp. schermo"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvataggio screenshot..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Salvataggio screenshot..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot in corso di salvataggio."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot acquisito."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tocca per visualizzare lo screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Impossibile acquisire lo screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Si è verificato un problema durante il salvataggio dello screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Salvataggio screenshot…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot salvato"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tocca per visualizzare lo screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossibile acquisire lo screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Si è verificato un problema durante il salvataggio dello screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opzioni trasferimento file USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Monta come lettore multimediale (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"I controlli di gestione delle notifiche ti consentono di impostare un livello di importanza compreso tra 0 e 5 per le notifiche di un\'app. \n\n"<b>"Livello 5"</b>" \n- Mostra in cima all\'elenco di notifiche \n- Consenti l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 4"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Visualizza sempre \n\n"<b>"Livello 3"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n\n"<b>"Livello 2"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n\n"<b>"Livello 1"</b>" \n- Impedisci l\'interruzione a schermo intero \n- Non visualizzare mai \n- Non emettere mai suoni e vibrazioni \n- Nascondi da schermata di blocco e barra di stato \n- Mostra in fondo all\'elenco di notifiche \n\n"<b>"Livello 0"</b>" \n- Blocca tutte le notifiche dell\'app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notifiche"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Non riceverai più queste notifiche"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorie di notifica"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Questa app non ha categorie di notifica"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Le notifiche di quest\'app non possono essere disattivate"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 categoria di notifica su <xliff:g id="NUMBER_1">%s</xliff:g> di questa app</item> - <item quantity="one">1 categoria di notifica su <xliff:g id="NUMBER_0">%s</xliff:g> di questa app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e altri <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e <xliff:g id="NUMBER_2">%3$d</xliff:g> altro</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Non vedrai più queste notifiche"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Continuare a ricevere queste notifiche?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Interrompi la ricezione di notifiche"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continua a mostrare"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuare a ricevere notifiche da questa app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Queste notifiche non possono essere disattivate"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> aperti"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> chiusi"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Consenti le notifiche di questo canale"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Tutte le categorie"</string> <string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizza: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizza"</string> <string name="notification_done" msgid="5279426047273930175">"Fine"</string> + <string name="inline_undo" msgid="558916737624706010">"Annulla"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"gestione delle notifiche"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opzioni di posticipazione notifiche"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 276fa3331894..4fd68c2caf8d 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"מתמשך"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"הודעות"</string> <string name="battery_low_title" msgid="6456385927409742437">"עוצמת הסוללה נמוכה"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"עוצמת הסוללה נמוכה. כדאי להפעיל את מצב החיסכון בסוללה"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>, נשארו בערך <xliff:g id="TIME">%s</xliff:g> על סמך השימוש במכשיר"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>, נשארו בערך <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>. הופעלה תכונת החיסכון בסוללה."</string> <string name="invalid_charger" msgid="4549105996740522523">"טעינה באמצעות USB אינה נתמכת.\nהשתמש אך ורק במטען שסופק."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"טעינה בחיבור USB אינה נתמכת."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"למשתמש המחובר לחשבון במכשיר הזה אין אפשרות להפעיל ניפוי באגים ב-USB. כדי להשתמש בתכונה הזו יש לעבור אל המשתמש הראשי."</string> <string name="compat_mode_on" msgid="6623839244840638213">"הגדל תצוגה כדי למלא את המסך"</string> <string name="compat_mode_off" msgid="4434467572461327898">"מתח כדי למלא את המסך"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"צילום מסך"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"שומר צילום מסך..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"שומר צילום מסך..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"מתבצעת שמירה של צילום המסך."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"צילום המסך בוצע."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"הקש כדי להציג את צילום המסך שלך."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"לא ניתן לבצע צילום מסך."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"התגלתה בעיה בשמירת צילום מסך."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"לא ניתן לשמור צילום מסך עקב שטח אחסון מוגבל."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"מתבצעת שמירה של צילום המסך"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"צילום המסך נשמר"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"אפשר להקיש כדי להציג את צילום המסך"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"לא ניתן לבצע צילום מסך"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"התגלתה בעיה בשמירת צילום המסך"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"לא ניתן לשמור צילום מסך עקב שטח אחסון מוגבל"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string> <string name="usb_preference_title" msgid="6551050377388882787">"אפשרויות העברת קבצים ב-USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"טען כנגן מדיה (MTP)"</string> @@ -561,30 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"כבוי"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של הודעות הפעלה, תוכל להגדיר רמת חשיבות מ-0 עד 5 להודעות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצג בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתר ממסך הנעילה ומשורת הסטטוס \n- הצג בתחתית רשימת ההודעות \n\n"<b>"רמה 0"</b>" \n- חסום את כל ההודעות מהאפליקציה"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"הודעות"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"לא תקבל את ההודעות האלה יותר"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> קטגוריות של הודעות"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"האפליקציה הזו לא תומכת בקטגוריות של הודעות"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"לא ניתן לכבות הודעות של האפליקציה הזאת"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="two">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_1">%s</xliff:g> מאפליקציה זו</item> - <item quantity="many">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_1">%s</xliff:g> מאפליקציה זו</item> - <item quantity="other">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_1">%s</xliff:g> מאפליקציה זו</item> - <item quantity="one">קטגוריית הודעות אחת מתוך <xliff:g id="NUMBER_0">%s</xliff:g> מאפליקציה זו</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="two"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ו-<xliff:g id="NUMBER_5">%3$d</xliff:g> אחרים</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ו-,<xliff:g id="NUMBER_5">%3$d</xliff:g> אחרים</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ו-<xliff:g id="NUMBER_5">%3$d</xliff:g> אחרים</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> ו-<xliff:g id="NUMBER_2">%3$d</xliff:g> אחר</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"לא תראה יותר את ההודעות האלה"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"להמשיך להציג את ההודעות האלה?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"הפסקת הודעות"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"להמשיך להציג"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"להמשיך להציג הודעות מהאפליקציה הזאת?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"לא ניתן לכבות את ההודעות האלה"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נפתחו"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נסגרו"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"התר הודעות מערוץ זה"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"כל הקטגוריות"</string> <string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"התאמה אישית: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"התאמה אישית"</string> <string name="notification_done" msgid="5279426047273930175">"סיום"</string> + <string name="inline_undo" msgid="558916737624706010">"ביטול"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"בקרת הודעות"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"אפשרויות של דחיית הודעות לטיפול בהמשך"</string> @@ -743,8 +736,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"הרחב"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"מזער"</string> <string name="pip_phone_close" msgid="8416647892889710330">"סגור"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"הגדרות"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"גרור למטה כדי לסגור"</string> <string name="pip_menu_title" msgid="4707292089961887657">"תפריט"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 1cff43b491df..983c014aaace 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="6456385927409742437">"電池残量が少なくなっています"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"電池残量が少なくなっています。バッテリー セーバーを ON にしてください"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"残量が<xliff:g id="PERCENTAGE">%s</xliff:g>です"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"残量 <xliff:g id="PERCENTAGE">%s</xliff:g>、約 <xliff:g id="TIME">%s</xliff:g>(使用状況に基づく)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"残量 <xliff:g id="PERCENTAGE">%s</xliff:g>、約 <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"残量が <xliff:g id="PERCENTAGE">%s</xliff:g> です。バッテリー セーバーは ON です。"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB充電には対応していません。\n付属の充電器をお使いください。"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB充電には対応していません。"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"この端末に現在ログインしているユーザーでは、USB デバッグを ON にすることはできません。この機能を使用するには、メインユーザーに切り替えてください。"</string> <string name="compat_mode_on" msgid="6623839244840638213">"画面サイズに合わせて拡大"</string> <string name="compat_mode_off" msgid="4434467572461327898">"画面サイズに合わせて拡大"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"スクリーンショット"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"スクリーンショットを保存中..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"スクリーンショットを保存しています..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"スクリーンショットを保存しています。"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットを取得しました。"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"タップするとスクリーンショットが表示されます。"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"スクリーンショットをキャプチャできませんでした。"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"スクリーンショットの保存中に問題が発生しました。"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"空き容量が足りないため、スクリーンショットを保存できません。"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"スクリーンショットを保存しています"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"スクリーンショットを保存しました"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"タップするとスクリーンショットが表示されます"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"スクリーンショットを撮影できませんでした"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"スクリーンショットの保存中に問題が発生しました"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"空き容量が足りないため、スクリーンショットを保存できません"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USBファイル転送オプション"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"メディアプレーヤー(MTP)としてマウント"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"OFF"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"電源通知管理では、アプリの通知の重要度をレベル 0~5 で設定できます。\n\n"<b>"レベル 5"</b>" \n- 通知リストの一番上に表示する \n- 全画面表示を許可する \n- 常にポップアップする \n\n"<b>"レベル 4"</b>" \n- 全画面表示しない \n- 常にポップアップする \n\n"<b>"レベル 3"</b>" \n- 全画面表示しない \n- ポップアップしない \n\n"<b>"レベル 2"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n\n"<b>"レベル 1"</b>" \n- 全画面表示しない \n- ポップアップしない \n- 音やバイブレーションを使用しない \n- ロック画面やステータスバーに表示しない \n- 通知リストの一番下に表示する \n\n"<b>"レベル 0"</b>" \n- アプリからのすべての通知をブロックする"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"今後、この通知は配信されません"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> 個の通知カテゴリ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"このアプリでは通知カテゴリが設定されていません"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"このアプリの通知を OFF にすることはできません"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">このアプリの通知カテゴリ <xliff:g id="NUMBER_1">%s</xliff:g> 件中 1 件</item> - <item quantity="one">このアプリの通知カテゴリ <xliff:g id="NUMBER_0">%s</xliff:g> 件中 1 件</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>、他 <xliff:g id="NUMBER_5">%3$d</xliff:g> 件</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>、他 <xliff:g id="NUMBER_2">%3$d</xliff:g> 件</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"今後、この通知は表示されません"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"この通知を今後も表示しますか?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"通知を表示しない"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"今後も表示する"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"このアプリからの通知を今後も表示しますか?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"この通知を OFF にすることはできません"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は開いています"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は閉じています"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"このチャンネルからの通知を許可する"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"すべてのカテゴリ"</string> <string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"カスタマイズ: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"カスタマイズ"</string> <string name="notification_done" msgid="5279426047273930175">"完了"</string> + <string name="inline_undo" msgid="558916737624706010">"元に戻す"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"通知管理"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"通知スヌーズ設定"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string> <string name="pip_phone_close" msgid="8416647892889710330">"閉じる"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"設定"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"下にドラッグして閉じる"</string> <string name="pip_menu_title" msgid="4707292089961887657">"メニュー"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>はピクチャー イン ピクチャーで表示中です"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 870c89632fc1..09c978f1a22c 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"მიმდინარე"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"შეტყობინებები"</string> <string name="battery_low_title" msgid="6456385927409742437">"ბატარეა იწურება"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ბატარეა იწურება. ჩართეთ ბატარეის დამზოგი"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"დარჩენილია <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"დარჩენილია <xliff:g id="PERCENTAGE">%s</xliff:g>, რაც დაახლოებით <xliff:g id="TIME">%s</xliff:g> არის, მოხმარების გათვალისწინებით"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"დარჩენილია <xliff:g id="PERCENTAGE">%s</xliff:g>, რაც დაახლოებით <xliff:g id="TIME">%s</xliff:g> არის"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"დარჩენილია <xliff:g id="PERCENTAGE">%s</xliff:g>. ბატარეის დამზოგი ჩართულია."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-ით დატენვა არ არის მხარდაჭერილი.\nგამოიყენეთ მხოლოდ ელექტრო-დამტენი."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB დატენვა მხარდაჭერილი არ არის."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ამ მოწყობილობაზე ამჟამად შესულ მომხმარებელს არ შეუძლია USB ხარვეზების გამართვის ფუნქციის ჩართვა. ამ ფუნქციის გამოსაყენებლად, მიუერთდით მთავარ მომხმარებელს."</string> <string name="compat_mode_on" msgid="6623839244840638213">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string> <string name="compat_mode_off" msgid="4434467572461327898">"გაწიეთ ეკრანის შესავსებად."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ეკრანის ანაბეჭდი"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"სკრინშოტის შენახვა…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ეკრანის სურათის შენახვა…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ეკრანის სურათი შენახულია."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"სკრინშოტი გადაღებულია."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"შეეხეთ ეკრანის ანაბეჭდის სანახავად."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ვერ მოხერხდა ეკრანის ანაბეჭდის გადაღება."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ეკრანის ანაბეჭდის შენახვისას წარმოიქმნა პრობლემა."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"ეკრანის ანაბეჭდი შენახულია"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"ეკრანის ანაბეჭდი შენახულია"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"შეეხეთ ეკრანის ანაბეჭდის სანახავად"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"ვერ მოხერხდა ეკრანის ანაბეჭდის გადაღება"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ეკრანის ანაბეჭდის შენახვისას წარმოიქმნა პრობლემა"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ფაილის ტრანსფერის პარამეტრები"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"მედია-საკრავად (MTP) ჩართვა"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"გამორთული"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"შეტყობინებების მართვის საშუალებების მეშვეობით, შეგიძლიათ განსაზღვროთ აპის შეტყობინებების მნიშვნელობის დონე 0-დან 5-მდე დიაპაზონში. \n\n"<b>"დონე 5"</b>" \n— შეტყობინებათა სიის თავში ჩვენება \n— სრულეკრანიანი რეჟიმის შეფერხების დაშვება \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 4"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე ყოველთვის გამოჩენა \n\n"<b>"დონე 3"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n\n"<b>"დონე 2"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n\n"<b>"დონე 1"</b>" \n— სრულეკრანიანი რეჟიმის შეფერხების აღკვეთა \n— ეკრანზე გამოჩენის აღკვეთა \n— ხმისა და ვიბრაციის აღკვეთა \n— ჩაკეტილი ეკრანიდან და სტატუსის ზოლიდან დამალვა \n— შეტყობინებათა სიის ბოლოში ჩვენება \n\n"<b>"დონე 0"</b>" \n— აპის ყველა შეტყობინების დაბლოკვა"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"შეტყობინებები"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"ამ შეტყობინებებს აღარ მიიღებთ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"შეტყობინებების <xliff:g id="NUMBER">%d</xliff:g> კატეგორია"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ამ აპს შეტყობინებების კატეგორიები არ აქვს"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ამ აპიდან შეტყობინებების გამორთვა ვერ მოხერხდება"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">ამ აპის შეტყობინებების <xliff:g id="NUMBER_1">%s</xliff:g> კატეგორიიდან 1</item> - <item quantity="one">ამ აპის შეტყობინებების <xliff:g id="NUMBER_0">%s</xliff:g> კატეგორიიდან 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> და <xliff:g id="NUMBER_5">%3$d</xliff:g> სხვა</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> და <xliff:g id="NUMBER_2">%3$d</xliff:g> სხვა</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"ამ შეტყობინებებს აღარ დაინახავთ"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"გაგრძელდეს ამ შეტყობინებათა ჩვენება?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"შეტყობინებების შეწყვეტა"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"ჩვენების გაგრძელება"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"გაგრძელდეს შეტყობინებათა ჩვენება ამ აპიდან?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ამ შეტყობინებათა გამორთვა ვერ მოხერხდება"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის გახსნილია"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის დახურულია"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ამ არხიდან შეტყობინებების დაშვება"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ყველა კატეგორია"</string> <string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"მორგება: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"მორგება"</string> <string name="notification_done" msgid="5279426047273930175">"მზადაა"</string> + <string name="inline_undo" msgid="558916737624706010">"მოქმედების გაუქმება"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"შეტყობინებების მართვის საშუალებები"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"შეტყობინებების ჩაჩუმების ვარიანტები"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"გაშლა"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ჩაკეცვა"</string> <string name="pip_phone_close" msgid="8416647892889710330">"დახურვა"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"პარამეტრები"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"დასახურად ჩავლებით ჩამოიტანეთ ქვემოთ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"მენიუ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> იყენებს რეჟიმს „ეკრანი ეკრანში“"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index c4ae759aa7a9..061a926e1e0a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ағымдағы"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Хабарлар"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батарея заряды төмен"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батарея заряды аз. \"Battery Saver\" функциясын қосыңыз"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Пайдалану барысына байланысты <xliff:g id="PERCENTAGE">%s</xliff:g> заряд, шамамен <xliff:g id="TIME">%s</xliff:g> қалды"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> заряд, шамамен <xliff:g id="TIME">%s</xliff:g> қалды"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Battery Saver қосулы."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB зарядтауды қолдау ұсынылмаған.\nЖабдықталған зарядтағыш құрылғысын ғана қолданыңыз."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB арқылы зарядтауға қолдау көрсетілмейді."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Бұл құрылғыға жаңа кірген пайдаланушы USB түзетуін іске қосылмайды. Бұл мүмкіндікті пайдалану үшін негізгі пайдаланушыға ауысыңыз."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Экранды толтыру үшін ұлғайту"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Экранды толтыру үшін созу"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Скриншотты сақтауда…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Скриншотты сақтауда…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Скриншот сақталуда."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сақталды."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Скриншотты көру үшін түртіңіз."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот жасалмады."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Скриншотты сақтау кезінде мәселе туындады."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Жадтағы шектеулі бос орынға байланысты скриншотты сақтау мүмкін емес."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Скриншот сақталуда"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Скриншот сақталды"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Скриншотты көру үшін түртіңіз"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Скриншот жасалмады"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Скриншотты сақтау кезінде ақау болды"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB файлын жіберу опциялары"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Медиа ойнатқыш (MTP) ретінде қосыңыз"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Өшірулі"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Қуат хабарландыруының басқару элементтерімен қолданбаның хабарландырулары үшін 0-ден бастап 5-ке дейін маңыздылық деңгейін орнатуға болады. \n\n"<b>"5-деңгей"</b>" \n- Хабарландыру тізімінің ең басында көрсету \n- Толық экранға ашылуын рұқсат ету \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"4-деңгей"</b>" \n- Толық экранға шығармау \n- Әрдайым қалқымалы хабарландыру түрінде көрсету \n\n"<b>"3-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n\n"<b>"2-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс және діріл шығармау \n\n"<b>"1-деңгей"</b>" \n- Толық экранға шығармау \n- Ешқашан қалқымалы хабарландыру түрінде көрсетпеу \n- Ешқашан дыбыс немесе діріл шығармау \n- Құлыпталған экраннан және күйін көрсету жолағынан жасыру \n- Хабарландыру тізімінің ең астында көрсету \n\n"<b>"0-деңгей"</b>" \n- Қолданбадағы барлық хабарландыруларға тыйым салу"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Хабарландырулар"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Енді сізге бұл хабарландырулар жіберілмейді"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> хабарландыру санаттары"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Бұл қолданбада хабарландыру санаттары жоқ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Бұл қолданбаның хабарландырулары өшірілмейді"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Осы қолданбадан <xliff:g id="NUMBER_1">%s</xliff:g> ішінен 1 хабарландыру санаты</item> - <item quantity="one">Осы қолданбадан <xliff:g id="NUMBER_0">%s</xliff:g> ішінен 1 хабарландыру санаты</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> және тағы <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> және тағы <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Хабарландырулар бұдан былай көрсетілмейді"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Хабарландырулар көрсетілсін бе?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Хабарландыруларға тыйым салу"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Көрсету"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Осы қолданбаның хабарландырулары көрсетілсін бе?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Хабарландыруларды өшіру мүмкін емес"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері ашылды"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері жабылды"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Осы арнадан келетін хабарландыруларға рұқсат беру"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Барлық санаттар"</string> <string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Реттеу: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Реттеу"</string> <string name="notification_done" msgid="5279426047273930175">"Дайын"</string> + <string name="inline_undo" msgid="558916737624706010">"Қайтару"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"хабарландыруларды басқару элементтері"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"хабарландыруды кідірту опциялары"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Жаю"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Кішірейту"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Жабу"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Реттеулер"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Жабу үшін төмен қарай сүйреңіз"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Mәзір"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> \"сурет ішіндегі сурет\" режимінде"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 12733f1c9e0e..175e10cc1649 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"បន្ត"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ការជូនដំណឹង"</string> <string name="battery_low_title" msgid="6456385927409742437">"ជិតអស់ថ្មហើយ"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"កម្រិតថ្មនៅសល់តិច។ សូមបើកកម្មវិធីសន្សំថ្ម"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"នៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"នៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g> អាចប្រើបានប្រហែល <xliff:g id="TIME">%s</xliff:g> ទៀតផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"នៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g> អាចប្រើបានប្រហែល <xliff:g id="TIME">%s</xliff:g> ទៀត"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"នៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g> ។ កម្មវិធីសន្សំថ្មបានបើក។"</string> <string name="invalid_charger" msgid="4549105996740522523">"មិនគាំទ្រការបញ្ចូលតាមយូអេសប៊ី។\nប្រើតែឧបករណ៍បញ្ចូលថ្មដែលបានផ្ដល់។"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"មិនគាំទ្រការបញ្ចូលថ្មតាមយូអេសប៊ីទេ។"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"អ្នកប្រើដែលបច្ចុប្បន្នបានចូលគណនីនៅលើឧបករណ៍នេះមិនអាចបើកការកែកំហុស USB បានទេ។ ដើម្បីប្រើមុខងារនេះ សូមប្តូរទៅអ្នកប្រើចម្បង។"</string> <string name="compat_mode_on" msgid="6623839244840638213">"ពង្រីកដើម្បីឲ្យពេញអេក្រង់"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ទាញដើម្បីឲ្យពេញអេក្រង់"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"រូបថតអេក្រង់"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថតអេក្រង់កំពុងត្រូវបានរក្សាទុក។"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ប៉ះដើម្បីមើលរូបថតអេក្រង់របស់អ្នក"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"បានជួបប្រទះបញ្ហាខណៈពេលរក្សាទុកការថតអេក្រង់"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេដោយសារទំហំផ្ទុកមានកម្រិត។"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"កំពុងរក្សាទុករូបថតអេក្រង់"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"បានរក្សាទុករូបថតអេក្រង់"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"ចុចដើម្បីមើលរូបថតអេក្រង់របស់អ្នក"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"មិនអាចថតរូបអេក្រង់បានទេ"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"បានជួបបញ្ហាពេលកំពុងរក្សាទុករូបថតអេក្រង់"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេ ដោយសារទំហំផ្ទុកមានកម្រិតទាប"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ការថតរូបអេក្រង់មិនត្រូវបានអនុញ្ញាតដោយកម្មវិធីនេះ ឬស្ថាប័នរបស់អ្នក"</string> <string name="usb_preference_title" msgid="6551050377388882787">"ជម្រើសផ្ទេរឯកសារតាមយូអេសប៊ី"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ភ្ជាប់ជាកម្មវិធីចាក់មេឌៀ (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"បិទ"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ជាមួយអង្គគ្រប់គ្រងការជូនដំណឹងថាមពល អ្នកអាចកំណត់កម្រិតសំខាន់ពី 0 ទៅ 5 សម្រាប់ការជូនដំណឹងរបស់កម្មវិធី។ \n\n"<b>"កម្រិត 5"</b>" \n- បង្ហាញនៅផ្នែកខាងលើបញ្ជីជូនដំណឹង \n- អនុញ្ញាតការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 4"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 3"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n\n"<b>"កម្រិត 2"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n\n"<b>"កម្រិត 1"</b>" \n- រារាំងការរំខានលើអេក្រង់ពេញ \n- លោតឡើងជានិច្ច \n- មិនបន្លឺសំឡេង ឬញ័រ \n- លាក់ពីអេក្រង់ចាក់សោ និងរបារស្ថានភាព \n- បង្ហាញនៅផ្នែកខាងក្រោមបញ្ជីជូនដំណឹង \n\n"<b>"កម្រិត 0"</b>" \n- រារាំងការជូនដំណឹងទាំងអស់ពីកម្មវិធី"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"ការជូនដំណឹង"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"អ្នកនឹងមិនទទួលបានការជូនដំណឹងទាំងនេះទៀតទេ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"ប្រភេទនៃការជូនដំណឹងចំនួន <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"កម្មវិធីនេះមិនមានប្រភេទនៃការជូនដំណឹងទេ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ការជូនដំណឹងពីកម្មវិធីនេះមិនអាចបិទបានទេ"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">ប្រភេទនៃការជូនដំណឹង 1 ក្នុងចំណោម <xliff:g id="NUMBER_1">%s</xliff:g> ដែលបានពីកម្មវិធីនេះ</item> - <item quantity="one">ប្រភេទនៃការជូនដំណឹង 1 ក្នុងចំណោម <xliff:g id="NUMBER_0">%s</xliff:g> ដែលបានពីកម្មវិធីនេះ</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, និង <xliff:g id="NUMBER_5">%3$d</xliff:g> ផ្សេងទៀត</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, និង <xliff:g id="NUMBER_2">%3$d</xliff:g> ផ្សេងទៀត</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"អ្នកនឹងមិនឃើញការជូនដំណឹងទាំងនេះទៀតទេ"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"បន្តបង្ហាញការជូនដំណឹងទាំងនេះ?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"បញ្ឈប់ការជូនដំណឹង"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"បន្តបង្ហាញ"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"បន្តបង្ហាញការជូនដំណឹងសម្រាប់កម្មវិធីនេះ?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"មិនអាចបិទការជូនដំណឹងទាំងនេះបានទេ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបើក"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបិទ"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"អនុញ្ញាតឲ្យមានការជូនដំណឹងពីប៉ុស្តិ៍នេះ"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ប្រភេទទាំងអស់"</string> <string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ប្ដូរតាមបំណង៖ <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"ប្ដូរតាមបំណង"</string> <string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string> + <string name="inline_undo" msgid="558916737624706010">"ត្រឡប់វិញ"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ការគ្រប់គ្រងការជូនដំណឹង"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ជម្រើសផ្អាកការជូនដំណឹង"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ពង្រីក"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"បង្រួម"</string> <string name="pip_phone_close" msgid="8416647892889710330">"បិទ"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ការកំណត់"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"អូសចុះក្រោមដើម្បីបដិសេធ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"ម៉ឺនុយ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ស្ថិតក្នុងមុខងាររូបក្នុងរូប"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index b492fd4bac94..3ce5e767228f 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ಅಧಿಸೂಚನೆಗಳು"</string> <string name="battery_low_title" msgid="6456385927409742437">"ಬ್ಯಾಟರಿ ಕಡಿಮೆ ಇದೆ"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಉಳಿದಿದೆ"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಉಳಿದಿದೆ. ಬ್ಯಾಟರಿ ಉಳಿತಾಯ ಆನ್ ಆಗಿದೆ."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ಚಾರ್ಜಿಂಗ್ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ.\nಒದಗಿಸಿರುವ ಚಾರ್ಜರ್ ಮಾತ್ರ ಬಳಸಿ."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ಚಾರ್ಜಿಂಗ್ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ಬಳಕೆದಾರರು ಪ್ರಸ್ತುತ ಈ ಸಾಧನಕ್ಕೆ ಸೈನ್ ಇನ್ ಮಾಡಿದ್ದಾರೆ USB ಡೀಬಗ್ ಮಾಡುವುದನ್ನು ಆನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲು, ಪ್ರಾಥಮಿಕ ಬಳಕೆದಾರರಿಗೆ ಬದಲಾಯಿಸಿ."</string> <string name="compat_mode_on" msgid="6623839244840638213">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ಶಾಟ್ ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸುವಲ್ಲಿ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ಫೈಲ್ ವರ್ಗಾವಣೆ ಆಯ್ಕೆಗಳು"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ಮೀಡಿಯಾ ಪ್ಲೇಯರ್ ರೂಪದಲ್ಲಿ ಅಳವಡಿಸಿ (MTP)"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ಆಫ್"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ಪವರ್ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳ ಮೂಲಕ, ನೀವು ಅಪ್ಲಿಕೇಶನ್ಗಳ ಅಧಿಸೂಚನೆಗಳನ್ನು 0 ರಿಂದ 5 ರವರೆಗಿನ ಹಂತಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಬಹುದು. \n\n"<b>"ಹಂತ 5"</b>" \n- ಮೇಲಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ಅನುಮತಿಸಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ \n\n"<b>"ಹಂತ 4"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಯಾವಾಗಲು ಇಣುಕು ನೋಟ\n\n"<b>"ಹಂತ 3"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n\n"<b>"ಹಂತ 2"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n\n"<b>"ಹಂತ 1"</b>" \n- ಪೂರ್ಣ ಪರದೆ ಅಡಚಣೆಯನ್ನು ತಡೆಯಿರಿ \n- ಎಂದಿಗೂ ಇಣುಕು ನೋಟ ಬೇಡ \n- ಶಬ್ದ ಮತ್ತು ವೈಬ್ರೇಷನ್ ಎಂದಿಗೂ ಮಾಡಬೇಡಿ \n- ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಪರದೆಯಿಂದ ಮರೆಮಾಡಿ \n- ಕೆಳಗಿನ ಅಧಿಸೂಚನೆ ಪಟ್ಟಿಯನ್ನು ತೋರಿಸಿ \n\n"<b>"ಹಂತ 0"</b>" \n- ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"ಅಧಿಸೂಚನೆಗಳು"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"ನೀವು ಇನ್ನು ಮುಂದೆ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಪಡೆಯುವುದಿಲ್ಲ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳು"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅಧಿಸೂಚನೆ ವರ್ಗಗಳನ್ನು ಹೊಂದಿಲ್ಲ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ <xliff:g id="NUMBER_1">%s</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳಲ್ಲಿ 1 ವರ್ಗ</item> - <item quantity="other">ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ <xliff:g id="NUMBER_1">%s</xliff:g> ಅಧಿಸೂಚನೆ ವರ್ಗಗಳಲ್ಲಿ 1 ವರ್ಗ</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ಮತ್ತು ಇತರ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ಮತ್ತು ಇತರ <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆರೆಯಲಾಗಿದೆ"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ಈ ಚಾನಲ್ನ ಅಧಿಸೂಚನೆಗಳಿಗೆ ಅನುಮತಿ ನೀಡಿ"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ಎಲ್ಲ ವರ್ಗಗಳು"</string> <string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ಕಸ್ಟಮೈಸ್: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ಅಧಿಸೂಚನೆ ಸ್ನೂಜ್ ಆಯ್ಕೆಗಳು"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ವಿಸ್ತೃತಗೊಳಿಸು"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ಕುಗ್ಗಿಸಿ"</string> <string name="pip_phone_close" msgid="8416647892889710330">"ಮುಚ್ಚಿ"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ವಜಾಗೊಳಿಸಲು ಕೆಳಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"ಮೆನು"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ಚಿತ್ರದಲ್ಲಿನ ಚಿತ್ರದಲ್ಲಿದೆ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 7fa87ed56d48..285c56970ebe 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string> <string name="battery_low_title" msgid="6456385927409742437">"배터리 부족"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"배터리가 부족합니다. 배터리 세이버를 사용 설정하세요"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남았습니다."</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남음, 내 사용량을 기준으로 약 <xliff:g id="TIME">%s</xliff:g> 남음"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남음, 약 <xliff:g id="TIME">%s</xliff:g> 남음"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> 남았습니다. 배터리 세이버를 사용 중입니다."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB 충전이 지원되지 않습니다.\n제공된 충전기만 사용하세요."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB 충전은 지원되지 않습니다."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"현재 이 기기에 로그인한 사용자는 USB 디버깅을 사용 설정할 수 없습니다. 이 기능을 사용하려면 기본 사용자로 전환하세요."</string> <string name="compat_mode_on" msgid="6623839244840638213">"전체화면 모드로 확대"</string> <string name="compat_mode_off" msgid="4434467572461327898">"전체화면 모드로 확대"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"스크린샷"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"캡쳐화면 저장 중..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"캡쳐화면 저장 중..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"캡쳐화면을 저장하는 중입니다."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"캡쳐화면 저장됨"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"스크린샷을 확인하려면 탭하세요."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"캡쳐화면을 캡쳐하지 못했습니다."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"스크린샷을 저장하는 중 문제가 발생했습니다."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"스크린샷을 저장하는 중입니다"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"스크린샷 저장됨"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"스크린샷을 확인하려면 탭하세요"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"스크린샷을 캡쳐하지 못함"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"스크린샷을 저장하는 중 문제가 발생했습니다"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB 파일 전송 옵션"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"미디어 플레이어로 마운트(MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"사용 안함"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"전원 알림 컨트롤을 사용하면 앱 알림 관련 중요도를 0부터 5까지로 설정할 수 있습니다. \n\n"<b>"레벨 5"</b>" \n- 알림 목록 상단에 표시 \n- 전체 화면일 경우 알림 표시 허용 \n- 항상 엿보기 표시 \n\n"<b>"레벨 4"</b>" \n- 전체 화면에 알림 표시 금지 \n- 항상 엿보기 표시 \n\n"<b>"레벨 3"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n\n"<b>"레벨 2"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n\n"<b>"레벨 1"</b>" \n- 전체 화면에 알림 표시 금지 \n- 엿보기 표시 안함 \n- 소리나 진동으로 알리지 않음 \n- 잠금 화면 및 상태 표시줄에서 숨김 \n- 알림 목록 하단에 표시 \n\n"<b>"레벨 0"</b>" \n- 앱의 모든 알림 차단"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"알림"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"더 이상 다음의 알림을 받지 않습니다."</string> - <string name="notification_num_channels" msgid="2048144408999179471">"알림 카테고리 <xliff:g id="NUMBER">%d</xliff:g>개"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"이 앱에는 알림 카테고리가 없습니다."</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"이 앱의 알림을 사용 중지할 수 없습니다."</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">이 앱에서 <xliff:g id="NUMBER_1">%s</xliff:g>개의 알림 카테고리 중 1개가 정의됨</item> - <item quantity="one">이 앱에서 <xliff:g id="NUMBER_0">%s</xliff:g>개의 알림 카테고리 중 1개가 정의됨</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> 외 <xliff:g id="NUMBER_5">%3$d</xliff:g>개</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> 외 <xliff:g id="NUMBER_2">%3$d</xliff:g>개</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"더 이상 다음의 알림을 받지 않습니다"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"이 알림을 계속 표시하시겠습니까?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"알림 중지"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"계속 표시하기"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"이 앱의 알림을 계속 표시하시겠습니까?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"이 알림은 끌 수 없습니다"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 열었습니다."</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 닫았습니다."</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"이 채널의 알림을 허용합니다."</string> - <string name="notification_all_categories" msgid="5407190218055113282">"전체 카테고리"</string> <string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"맞춤설정: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"맞춤설정"</string> <string name="notification_done" msgid="5279426047273930175">"완료"</string> + <string name="inline_undo" msgid="558916737624706010">"실행취소"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"알림 관리"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"알림 일시 중지 옵션"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"펼치기"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"최소화"</string> <string name="pip_phone_close" msgid="8416647892889710330">"닫기"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"설정"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"아래로 드래그하여 닫기"</string> <string name="pip_menu_title" msgid="4707292089961887657">"메뉴"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index e3f1fa3eaa82..0687c2d30c97 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Учурдагы"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Эскертмелер"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батареянын кубаты аз"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батарея аз калды. Батареяны үнөмдөгүчтү күйгүзүңүз"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> калды"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> калды, колдонушуңузга караганда болжол менен дагы <xliff:g id="TIME">%s</xliff:g> бар"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> калды, болжол менен дагы <xliff:g id="TIME">%s</xliff:g> бар"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> калды. Батареяны үнөмдөгүч режими күйүк."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB менен кубаттоо колдоого алынбайт.\nБерилген заряддагычты гана колдонуңуз."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB аркылуу кубаттоого болбойт."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Учурда бул түзмөккө каттоо эсеби менен кирген колдонуучу USB мүчүлүштүктөрүн оңдоо функциясын күйгүзө албай жатат. Бул функцияны колдонуу үчүн негизги колдонуучунун каттоо эсебине которулуңуз."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Экрнд тлтр ү. чен өлч өзг"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Экранды толтуруу ү-н чоюу"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Скриншот сакталууда…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Скриншот сакталууда..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Скриншот сакталууда."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот тартылды."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Скриншотуңузду көрүү үчүн таптап коюңуз."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот кылынбай жатат."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Скриншотту сактоо учурунда көйгөй пайда болду."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Сактагычта бош орун аз болгондуктан скриншот сакталбай жатат."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Скриншот сакталууда"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Скриншот сакталды"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Скриншотуңузду көрүү үчүн таптап коюңуз"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Скриншот тартылбай жатат"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Скриншотту сактоо учурунда көйгөй пайда болду"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Сактагычта бош орун аз болгондуктан скриншот сакталбай жатат"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB менен файл өткөрүү мүмкүнчүлүктөрү"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Медиа ойноткуч катары кошуу (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Өчүк"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Бул функциянын жардамы менен ар бир колдонмо үчүн эскертменин маанилүүлүк деңгээлин 0дон 5ке чейин койсоңуз болот. \n\n"<b>"5-деңгээл"</b>" \n- Эскертмелер тизмесинин башында көрсөтүлсүн \n- Эскертмелер толук экранда көрсөтүлсүн \n- Калкып чыгуучу эскертмелерге уруксат берилсин \n\n"<b>"4-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге уруксат берилсин \n\n"<b>"3-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n\n"<b>"2-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n- Эч качан добуш чыгып же дирилдебесин \n\n"<b>"1-деңгээл"</b>" \n- Эскертмелер толук экранда көрсөтүлбөсүн \n- Калкып чыгуучу эскертмелерге тыюу салынсын \n- Эч качан добуш чыгып же дирилдебесин \n- Кулпуланган экрандан жана абал тилкесинен жашырылсын \n- Эскертмелер тизмесинин аягында көрсөтүлсүн \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык эскертмелер бөгөттөлсүн"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Эскертмелер"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Мындан ары бул эскертмелер сизге жөнөтүлбөйт"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Эскертмелердин <xliff:g id="NUMBER">%d</xliff:g> категориясы"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Бул колдонмонун эскертме категориялары жок"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Бул колдонмонун эскертмелерин өчүрүүгө болбойт"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Бул колдонмодогу <xliff:g id="NUMBER_1">%s</xliff:g> эскертме категориянын ичинен 1 категория</item> - <item quantity="one">Бул колдонмодогу <xliff:g id="NUMBER_0">%s</xliff:g> эскертме категориянын ичинен 1 категория</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> жана дагы <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> жана дагы <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Мындан ары бул эскертмелер сизге көрсөтүлбөйт"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Бул эскертмелер көрсөтүлө берсинби?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Эскертмелерди токтотуу"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Көрсөтүлө берсин"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Бул колдонмонун эскертмелери көрсөтүлө берсинби?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Бул эскертмелерди өчүрүүгө болбойт"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары ачылды"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары жабылды"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Бул каналдан келген эскертмелерге уруксат берүү"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Бардык категориялар"</string> <string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Ыңгайлаштыруу: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Ыңгайлаштыруу"</string> <string name="notification_done" msgid="5279426047273930175">"Бүттү"</string> + <string name="inline_undo" msgid="558916737624706010">"Кайтаруу"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"эскертмелерди башкаруу каражаттары"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"эскертмени тындыруу опциялары"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Жайып көрсөтүү"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Кичирейтүү"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Жабуу"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Жөндөөлөр"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Четке кагуу үчүн төмөн сүйрөңүз"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index fe262ee053a3..587686fa5046 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ດຳເນີນຢູ່"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ການແຈ້ງເຕືອນ"</string> <string name="battery_low_title" msgid="6456385927409742437">"ແບັດເຕີຣີເຫຼືອໜ້ອຍ"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ແບັດເຕີຣີເຫຼືອໜ້ອຍ. ກະລຸນາເປີດໃຊ້ຕົວປະຢັດແບັດເຕີຣີ"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"ຍັງເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"ຍັງເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>, ປະມານ <xliff:g id="TIME">%s</xliff:g> ໂດຍອ້າງອີງຈາກການນຳໃຊ້ຂອງທ່ານ"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"ຍັງເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>, ປະມານ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"ຍັງເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>. ເປີດໃຊ້ຕົວປະຢັດແບັດເຕີຣີແລ້ວ."</string> <string name="invalid_charger" msgid="4549105996740522523">"ບໍ່ຮອງຮັບການສາກໄຟດ້ວຍ USB.\nຕ້ອງໃຊ້ສະເພາະເຄື່ອງສາກທີ່ແຖມມານຳເທົ່ານັ້ນ."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"ບໍ່ຮອງຮັບການສາກຜ່ານ USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ຜູ້ໃຊ້ທີ່ກຳລັງເຂົ້າສູ່ລະບົບອຸປະກອນຢູ່ໃນຕອນນີ້ບໍ່ສາມາດເປີດໃຊ້ການດີບັກ USB ໄດ້. ເພື່ອໃຊ້ຄຸນສົມບັດນີ້, ໃຫ້ສະຫຼັບໄປໃຊ້ຜູ້ໃຊ້ຫຼັກ."</string> <string name="compat_mode_on" msgid="6623839244840638213">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ພາບໜ້າຈໍ"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ກຳລັງບັນທຶກຮູບໜ້າຈໍ"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ກຳລັງບັນທຶກພາບໜ້າຈໍ."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ຖ່າຍຮູບໜ້າຈໍແລ້ວ"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ແຕະເພື່ອເບິ່ງພາບຖ່າຍໜ້າຈໍຂອງທ່ານ."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ເກີດບັນຫາໃນການບັນທຶກພາບໜ້າຈໍຂອງທ່ານ."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"ກຳລັງບັນທຶກພາບໜ້າຈໍ"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"ບັນທຶກຮູບໜ້າຈໍໄວ້ແລ້ວ"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"ແຕະເພື່ອເບິ່ງພາບຖ່າຍໜ້າຈໍຂອງທ່ານ"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ເກີດບັນຫາໃນການບັນທຶກພາບໜ້າຈໍຂອງທ່ານ"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຖ່າຍຮູບໜ້າຈໍ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ໂຕເລືອກການຍ້າຍໄຟລ໌"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ເຊື່ອມຕໍ່ເປັນ media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ປິດ"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ດ້ວຍການຄວບຄຸມການແຈ້ງເຕືອນ, ທ່ານສາມາດຕັ້ງລະດັບຄວາມສຳຄັນຈາກ 0 ຮອດ 5 ໃຫ້ກັບການແຈ້ງເຕືອນແອັບໃດໜຶ່ງໄດ້. \n\n"<b>"ລະດັບ 5"</b>" \n- ສະແດງຢູ່ເທິງສຸດຂອງລາຍການແຈ້ງເຕືອນ \n- ອະນຸຍາດໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 4"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ແນມເບິ່ງທຸກເທື່ອ \n\n"<b>"ລະດັບ 3"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n\n"<b>"ລະດັບ 2"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n\n"<b>"ລະດັບ 1"</b>" \n- ກັນບໍ່ໃຫ້ຂັດຈັງຫວະຕອນເປີດເຕັມຈໍ \n- ບໍ່ແນມເບິ່ງ \n- ບໍ່ມີສຽງ ແລະ ບໍ່ມີການສັ່ນເຕືອນ \n- ເຊື່ອງຈາກໜ້າຈໍລັອກ ແລະ ແຖບສະຖານະ \n- ສະແດງຢູ່ລຸ່ມສຸດຂອງລາຍການແຈ້ງເຕືອນ \n\n"<b>"ລະດັບ 0"</b>" \n- ປິດກັ້ນການແຈ້ງເຕືອນທັງໝົດຈາກແອັບ"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"ການແຈ້ງເຕືອນ"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"ທ່ານຈະບໍ່ໄດ້ຮັບການແຈ້ງເຕືອນເຫຼົ່ານີ້ອີກຕໍ່ໄປ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ໝວດໝູ່ການແຈ້ງເຕືອນ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ແອັບນີ້ບໍ່ມີໝວດໝູ່ການແຈ້ງເຕືອນ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ການແຈ້ງເຕືອນຈາກແອັບນີ້ບໍ່ສາມາດປິດໄວ້ໄດ້"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 ຈາກທັງໝົດ <xliff:g id="NUMBER_1">%s</xliff:g> ໝວດໝູ່ການແຈ້ງເຕືອນຈາກແອັບນີ້</item> - <item quantity="one">1 ຈາກທັງໝົດ <xliff:g id="NUMBER_0">%s</xliff:g> ໝວດໝູ່ການແຈ້ງເຕືອນຈາກແອັບນີ້</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ແລະ ອີກ <xliff:g id="NUMBER_5">%3$d</xliff:g> ຊ່ອງອື່ນໆ</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> ແລະ ອີກ <xliff:g id="NUMBER_2">%3$d</xliff:g> ຊ່ອງອື່ນໆ</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"ທ່ານຈະບໍ່ໄດ້ຮັບການແຈ້ງເຕືອນເຫຼົ່ານີ້ອີກຕໍ່ໄປ"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"ສະແດງການແຈ້ງເຕືອນເຫຼົ່ານີ້ຕໍ່ໄປບໍ?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"ຢຸດການແຈ້ງເຕືອນ"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"ສະແດງຕໍ່ໄປ"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"ສະແດງການແຈ້ງເຕືອນຈາກແອັບນີ້ຕໍ່ໄປບໍ?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ບໍ່ສາມາດປິດການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"ເປີດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"ປິດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ອະນຸຍາດການແຈ້ງເຕືອນຈາກຊ່ອງນີ້"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ທຸກໝວດໝູ່"</string> <string name="notification_more_settings" msgid="816306283396553571">"ການຕັ້ງຄ່າເພີ່ມເຕີມ"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ປັບແຕ່ງ: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"ປັບແຕ່ງ"</string> <string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string> + <string name="inline_undo" msgid="558916737624706010">"ຍົກເລີກ"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ການຄວບຄຸມການແຈ້ງເຕືອນ"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ຕົວເລືອກການເລື່ອນການແຈ້ງເຕືອນ"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ຂະຫຍາຍ"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ຫຍໍ້"</string> <string name="pip_phone_close" msgid="8416647892889710330">"ປິດ"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ການຕັ້ງຄ່າ"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ລາກລົງເພື່ອປິດໄວ້"</string> <string name="pip_menu_title" msgid="4707292089961887657">"ເມນູ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ແມ່ນເປັນການສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 5530fc96649f..a613b2315980 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Vykstantys"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pranešimai"</string> <string name="battery_low_title" msgid="6456385927409742437">"Akumuliatorius senka"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akumuliatorius senka. Įjunkite Akumuliatoriaus tausojimo priemonę"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Liko <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Liko: <xliff:g id="PERCENTAGE">%s</xliff:g> (atsižvelgiant į naudojimą liko maždaug <xliff:g id="TIME">%s</xliff:g>)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Liko: <xliff:g id="PERCENTAGE">%s</xliff:g> (liko maždaug <xliff:g id="TIME">%s</xliff:g>)"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Liko <xliff:g id="PERCENTAGE">%s</xliff:g>. Akumuliatoriaus tausojimo priemonė įjungta."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB krovimas nepalaikomas.\nNaudokite tik pateiktą įkroviklį."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB įkrovimas nepalaikomas."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Šiuo metu prie įrenginio prisijungęs naudotojas negali įjungti USB derinimo. Kad galėtumėte naudoti šią funkciją, perjunkite į pagrindinį naudotoją."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Keisti mast., kad atit. ekr."</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ištempti, kad atit. ekr."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrano kopija"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Išsaugoma ekrano kopija..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Išsaugoma ekrano kopija..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Išsaugoma ekrano kopija."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrano kopija užfiksuota."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Palieskite, kad peržiūrėtumėte ekrano kopiją."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nepavyko užfiksuoti ekrano kopijos."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Išsaugant ekrano kopiją iškilo problemų."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Ekrano kopija išsaugoma"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Ekrano kopija išsaugota"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Palieskite, kad peržiūrėtumėte ekrano kopiją"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Nepavyko užfiksuoti ekrano kopijos"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Išsaugant ekrano kopiją kilo problema"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB failo perdavimo parinktys"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Įmontuoti kaip medijos leistuvę (MTP)"</string> @@ -561,30 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Išjungta"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Naudodami pranešimų valdiklius galite nustatyti programos pranešimų svarbos lygį nuo 0 iki 5. \n\n"<b>"5 lygis"</b>" \n– Rodyti pranešimų sąrašo viršuje \n– Leisti pertraukti, kai veikia viso ekrano režimas \n– Visada rodyti pranešimus \n\n"<b>"4 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Visada rodyti pranešimus \n\n"<b>"3 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n\n"<b>"2 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n\n"<b>"1 lygis"</b>" \n– Neleisti pertraukti viso ekrano režimo \n– Niekada nerodyti pranešimų \n– Niekada neleisti garso ir nevibruoti \n– Slėpti užrakinimo ekrane ir būsenos juostoje \n– Rodyti pranešimų sąrašo apačioje \n\n"<b>"0 lygis"</b>" \n– Blokuoti visus programos pranešimus"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Pranešimai"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Nebegausite šių pranešimų"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Pranešimų kategorijų: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Šioje programoje nėra pranešimų kategorijų"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Šios programos pranešimų negalima išjungti"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijos</item> - <item quantity="few">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijų</item> - <item quantity="many">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijos</item> - <item quantity="other">1 iš <xliff:g id="NUMBER_1">%s</xliff:g> šios programos pranešimų kategorijų</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"„<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>“"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalas</item> - <item quantity="few">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalai</item> - <item quantity="many">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalo</item> - <item quantity="other">„<xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>“, „<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>“ ir dar <xliff:g id="NUMBER_5">%3$d</xliff:g> kanalų</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Nebematysite šių pranešimų"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Toliau rodyti šiuos pranešimus?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Sustabdyti pranešimus"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Toliau rodyti"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Toliau rodyti iš šios programos gautus pranešimus?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Šių pranešimų negalima išjungti"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai atidaryti"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai uždaryti"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Leisti pranešimus iš šio kanalo"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Visos kategorijos"</string> <string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Tinkinti: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Tinkinti"</string> <string name="notification_done" msgid="5279426047273930175">"Atlikta"</string> + <string name="inline_undo" msgid="558916737624706010">"Anuliuoti"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"pranešimų valdikliai"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"pranešimų snaudimo parinktys"</string> @@ -743,8 +736,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Išskleisti"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Sumažinti"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Uždaryti"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Nustatymai"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Nuvilkite žemyn, kad atsisakytumėte"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meniu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 574d898e311c..a02a20bcda5b 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Notiekošs"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Paziņojumi"</string> <string name="battery_low_title" msgid="6456385927409742437">"Zems akumulatora enerģijas līmenis"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akumulatora uzlādes līmenis ir zems. Ieslēdziet akumulatora jaudas taupīšanas režīmu."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Atlikuši <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Atlikušais laiks: <xliff:g id="PERCENTAGE">%s</xliff:g> — aptuveni <xliff:g id="TIME">%s</xliff:g> (ņemot vērā lietojumu)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Atlikušais laiks: <xliff:g id="PERCENTAGE">%s</xliff:g> — aptuveni <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Atlikuši <xliff:g id="PERCENTAGE">%s</xliff:g>. Ir ieslēgts akumulatora jaudas taupīšanas režīms."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB lādēšana netiek atbalstīta.\nIzmantojiet tikai komplektā iekļauto lādētāju."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB uzlāde netiek atbalstīta."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Lietotājs, kurš pašlaik ir pierakstījies šajā ierīcē, nevar iespējot USB atkļūdošanu. Lai izmantotu šo funkciju, pārslēdzieties uz galveno lietotāju."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Tālumm., lai aizp. ekr."</string> <string name="compat_mode_off" msgid="4434467572461327898">"Stiepiet, lai aizp. ekr."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ekrānuzņēmums"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saglabā ekrānuzņēmumu…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Notiek ekrānuzņēmuma saglabāšana..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Notiek ekrānuzņēmuma saglabāšana."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrānuzņēmums ir uzņemts."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Pieskarieties, lai skatītu ekrānuzņēmumu."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nevarēja uzņemt ekrānuzņēmumu."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Saglabājot ekrānuzņēmumu, radās problēma."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Notiek ekrānuzņēmuma saglabāšana."</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Ekrānuzņēmums saglabāts"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Pieskarieties, lai skatītu ekrānuzņēmumu."</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Nevarēja uzņemt ekrānuzņēmumu"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Saglabājot ekrānuzņēmumu, radās problēma."</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB failu pārsūtīšanas opcijas"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Pievienot kā multivides atskaņotāju (MTP)"</string> @@ -559,28 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Izslēgts"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Izmantojot barošanas paziņojumu vadīklas, varat lietotnes paziņojumiem iestatīt svarīguma līmeni (no 0 līdz 5). \n\n"<b>"5. līmenis"</b>" \n- Tiek rādīts paziņojumu saraksta augšdaļā \n- Tiek atļauta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"4. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats vienmēr atļauts \n\n"<b>"3. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n\n"<b>"2. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n\n"<b>"1. līmenis"</b>" \n- Tiek novērsta pilnekrāna režīma pārtraukšana \n- Ieskats nav atļauts \n- Nav atļautas skaņas un vibrosignāls \n- Paziņojumi tiek paslēpti bloķēšanas ekrānā un statusa joslā \n- Paziņojumi tiek rādīti paziņojumu saraksta apakšdaļā \n\n"<b>"0. līmenis"</b>" \n- Visi lietotnes paziņojumi tiek bloķēti"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Paziņojumi"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Jūs vairs nesaņemsiet šos paziņojumus"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> paziņojumu kategorijas"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Paziņojumu kategorijas šajā lietotnē nav pieejamas."</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Paziņojumus no šīs lietotnes nevar izslēgt"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="zero">1 no <xliff:g id="NUMBER_1">%s</xliff:g> šīs lietotnes paziņojumu kategorijām.</item> - <item quantity="one">1 no <xliff:g id="NUMBER_1">%s</xliff:g> šīs lietotnes paziņojumu kategorijas.</item> - <item quantity="other">1 no <xliff:g id="NUMBER_1">%s</xliff:g> šīs lietotnes paziņojumu kategorijām.</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="zero"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> un vēl <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> un vēl <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> un vēl <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Jūs vairs neredzēsiet šos paziņojumus."</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Vai turpināt rādīt šos paziņojumus?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Apturēt paziņojumu rādīšanu"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Turpināt rādīt"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vai turpināt rādīt paziņojumus no šīs lietotnes?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Šos paziņojumus nevar izslēgt."</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir atvērtas"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir aizvērtas"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Atļaut paziņojumus no šī kanāla"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Visas kategorijas"</string> <string name="notification_more_settings" msgid="816306283396553571">"Citi iestatījumi"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Pielāgot: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Pielāgot"</string> <string name="notification_done" msgid="5279426047273930175">"Gatavs"</string> + <string name="inline_undo" msgid="558916737624706010">"Atsaukt"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"paziņojumu vadīklas"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"paziņojumu atlikšanas opcijas"</string> @@ -737,8 +732,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Izvērst"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizēt"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Aizvērt"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Iestatījumi"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Velciet lejup, lai noraidītu"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Izvēlne"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b64217dc2774..886e083044b1 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Во тек"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Известувања"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батеријата е слаба"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батеријата е слаба. Вклучете го штедачот на батерија"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Преостануваат <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Преостануваат <xliff:g id="PERCENTAGE">%s</xliff:g>, уште околу <xliff:g id="TIME">%s</xliff:g> според користењето"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Преостануваат <xliff:g id="PERCENTAGE">%s</xliff:g>, уште околу <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Преостануваат <xliff:g id="PERCENTAGE">%s</xliff:g>. Штедачот на батерија е вклучен."</string> <string name="invalid_charger" msgid="4549105996740522523">"Полначот на USB меморијата не е поддржан.\nКористете го само полначот доставен со уредот."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Полнењето преку USB не е поддржано."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Корисникот што моментално е најавен на уредов не може да вклучи отстранување грешки на USB. За да ја користите функцијава, префрлете се на примарниот корисник."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Зумирај да се исполни екранот"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Растегни да се исполни екранот"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Слика од екранот"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Сликата на екранот се зачувува..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Сликата на екранот се зачувува..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Сликата на екранот се зачувува."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Сликата на екранот е снимена."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Допрете за да ја видите сликата на екранот."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Сликата на екранот не можеше да се сними."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Се појави проблем при зачувување на сликата од екранот."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Сликата од екранот не може да се зачува поради ограничена меморија."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Сликата од екранот се зачувува"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Сликата од екранот е зачувана"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Допрете за да ја видите сликата од екранот"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Сликата од екранот не можеше да се сними"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Проблем при зачувувањето слика од екранот"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Сликата од екранот не може да се зачува поради ограничена меморија"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Пренос на датотека со USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Монтирај како мултимедијален плеер (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Исклучено"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Со контролите за известувањата за напојување, може да поставите ниво на важност од 0 до 5 за известувањата на која било апликација. \n\n"<b>"Ниво 5"</b>" \n- Прикажувај на врвот на списокот со известувања \n- Дозволи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 4"</b>" \n- Спречи прекин во цел екран \n- Секогаш користи појавување \n\n"<b>"Ниво 3"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n\n"<b>"Ниво 2"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n\n"<b>"Ниво 1"</b>" \n- Спречи прекин во цел екран \n- Без појавување \n- Без звук и вибрации \n- Сокриј од заклучен екран и статусна лента \n- Прикажувај на дното на списокот со известувања \n\n"<b>"Ниво 0"</b>" \n- Блокирај ги сите известувања од апликацијата"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Известувања"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Веќе нема да ги добивате овие известувања"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> категории известувања"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Апликацијава нема катерии известувања"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Известувањата од апликацијава не може да се исклучат"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 од <xliff:g id="NUMBER_1">%s</xliff:g> категорија известувања од апликацијава</item> - <item quantity="other">1 од <xliff:g id="NUMBER_1">%s</xliff:g> категории известувања од апликацијава</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и уште <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и уште <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Веќе нема да ги гледате овие известувања"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Дали да продолжат да се прикажуваат известувањава?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Запри ги известувањата"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Продолжи да ги прикажуваш"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Дали да продолжат да се прикажуваат известувања од апликацијава?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Известувањава не може да се исклучат"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се отворија"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се затворија"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Дозволете известувања од овој канал"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Сите категории"</string> <string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Приспособи: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Приспособете"</string> <string name="notification_done" msgid="5279426047273930175">"Готово"</string> + <string name="inline_undo" msgid="558916737624706010">"Врати"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"контроли за известувањето"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"опции за одложување на известувањето"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Проширете"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Минимизирај"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Затвори"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Поставки"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Повлечете надолу за да отфрлите"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Мени"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> е во слика во слика"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index aba426097f26..f3fa9e9cd39f 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"നടന്നുകൊണ്ടിരിക്കുന്നവ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"അറിയിപ്പുകൾ"</string> <string name="battery_low_title" msgid="6456385927409742437">"ബാറ്ററി കുറവാണ്"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ശേഷിക്കുന്നു"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ശേഷിക്കുന്നു. ബാറ്ററി ലാഭിക്കൽ ഓണാണ്."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ചാർജ്ജുചെയ്യൽ പിന്തുണയ്ക്കുന്നില്ല.\nഅതിന്റെ അനുബന്ധ ചാർജ്ജർ മാത്രം ഉപയോഗിക്കുക."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ചാർജ്ജുചെയ്യൽ പിന്തുണച്ചില്ല."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ഉപകരണത്തിൽ ഇപ്പോൾ സൈൻ ഇൻ ചെയ്തിരിക്കുന്ന ഉപയോക്താവിന് USB ഡീബഗ്ഗിംഗ് ഓണാക്കാനാകില്ല. ഈ ഫീച്ചർ ഉപയോഗിക്കാൻ പ്രാഥമിക ഉപയോക്താവിലേക്ക് മാറുക."</string> <string name="compat_mode_on" msgid="6623839244840638213">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string> <string name="compat_mode_off" msgid="4434467572461327898">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"സ്ക്രീൻഷോട്ട് എടുത്തു."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"നിങ്ങളുടെ സ്ക്രീൻഷോട്ട് കാണുന്നതിന് ടാപ്പുചെയ്യുക."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിഞ്ഞില്ല."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്ന സമയത്ത് പ്രശ്നം നേരിട്ടു."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"സ്റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാൻ കഴിയില്ല."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ഫയൽ കൈമാറൽ ഓപ്ഷനുകൾ"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ഒരു മീഡിയ പ്ലേയറായി (MTP) മൗണ്ടുചെയ്യുക"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ഓഫ്"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"പവർ അറിയിപ്പ് നിയന്ത്രണം ഉപയോഗിച്ച്, ഒരു ആപ്പിനായുള്ള അറിയിപ്പുകൾക്ക് 0 മുതൽ 5 വരെയുള്ള പ്രാധാന്യ ലെവലുകളിലൊന്ന് നിങ്ങൾക്ക് സജ്ജമാക്കാവുന്നതാണ്. \n\n"<b>"ലെവൽ 5"</b>" \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ മുകളിൽ കാണിക്കുക \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം അനുവദിക്കുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 4"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- എല്ലായ്പ്പോഴും ദൃശ്യമാക്കുക \n\n"<b>"ലെവൽ 3"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും സൃശ്യമാക്കരുത് \n\n"<b>"ലെവൽ 2"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n\n"<b>"ലെവൽ 1"</b>" \n- മുഴുവൻ സ്ക്രീൻ തടസ്സം തടയുക \n- ഒരിക്കലും ദൃശ്യമാക്കരുത് \n- ഒരിക്കലും ശബ്ദവും വൈബ്രേഷനും ഉണ്ടാക്കരുത് \n- ലോക്ക് സ്ക്രീനിൽ നിന്നും സ്റ്റാറ്റസ് ബാറിൽ നിന്നും മറയ്ക്കുക \n- അറിയിപ്പ് ലിസ്റ്റിന്റെ അടിയിൽ കാണിക്കുക \n\n"<b>"ലെവൽ 0"</b>" \n- ആപ്പിൽ നിന്നുള്ള എല്ലാ അറിയിപ്പുകളും ബ്ലോക്കുചെയ്യുക"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"അറിയിപ്പുകൾ"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"നിങ്ങൾക്ക് ഇനി ഈ അറിയിപ്പുകൾ ലഭിക്കില്ല"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> അറിയിപ്പ് വിഭാഗങ്ങൾ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ഈ ആപ്പിന് അറിയിപ്പ് വിഭാഗങ്ങളില്ല"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ ഓഫാക്കാൻ കഴിയില്ല"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">ഈ ആപ്പിൽ നിന്ന് 1 / <xliff:g id="NUMBER_1">%s</xliff:g> അറിയിപ്പ് വിഭാഗങ്ങൾ</item> - <item quantity="one">ഈ ആപ്പിൽ നിന്ന് 1 / <xliff:g id="NUMBER_0">%s</xliff:g> അറിയിപ്പ് വിഭാഗം</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> എന്നിവയും മറ്റ് <xliff:g id="NUMBER_5">%3$d</xliff:g> എണ്ണവും</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> എന്നിവയും മറ്റ് <xliff:g id="NUMBER_2">%3$d</xliff:g> എണ്ണവും</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ തുറന്നു"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ അടച്ചു"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ഈ ചാനലിൽ നിന്നുള്ള അറിയിപ്പുകൾ അനുവദിക്കുക"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"എല്ലാ വിഭാഗങ്ങളും"</string> <string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ഇഷ്ടാനുസൃതമാക്കുക: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"അറിയിപ്പ് സ്നൂസ് ഓപ്ഷനുകൾ"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"വികസിപ്പിക്കുക"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ചെറുതാക്കുക"</string> <string name="pip_phone_close" msgid="8416647892889710330">"അവസാനിപ്പിക്കുക"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ക്രമീകരണം"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"തള്ളിക്കളയാൻ താഴേക്ക് വലിച്ചിടുക"</string> <string name="pip_menu_title" msgid="4707292089961887657">"മെനു"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിലെ ചിത്രത്തിലാണ്"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 42bd38b7271c..c8aba01b79e8 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -31,7 +31,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Гарсан"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Мэдэгдэл"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батерей дуусаж байна"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батерей бага байна. Тэжээл хэмнэгчийг асаана уу"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> үлдсэн"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> үлдсэн байна. Таны хэрэглээнд тулгуурлан ойролцоогоор <xliff:g id="TIME">%s</xliff:g>-н хугацаа үлдсэн"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> үлдсэн байна. Ойролцоогоор <xliff:g id="TIME">%s</xliff:g>-н хугацаа үлдсэн"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> үлдсэн. Тэжээл хэмнэгч асаалттай байна."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB цэнэглэлт дэмжигдэхгүй байна.\nЗөвхөн нийлүүлэгдсэн цэнэглэгчийг ашиглана уу."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB-р цэнэглэх дэмжигддэггүй."</string> @@ -65,14 +68,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Энэ төхөөрөмжид нэвтэрсэн хэрэглэгч USB дебаг хийх онцлогийг асаах боломжгүй байна. Энэ онцлогийг ашиглахын тулд үндсэн хэрэглэгч рүү сэлгэнэ үү."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Дэлгэц дүүргэх бол өсгөнө үү"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Дэлгэц дүүргэх бол татна уу"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Дэлгэцийн зураг дарах"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Дэлгэцийн агшинг хадгалж байна…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Дэлгэцийн агшинг хадгалж байна…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Дэлгэцийн агшин хадгалагдав."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Дэлгэцийн агшинг авсан."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Дэлгэцийн агшингаа харахын тулд дарна уу."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Дэлгэцийн агшинг авч чадсангүй."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Дэлгэцийн агшинг хадгалахад алдаа гарлаа."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Хадгалах сангийн багтаамж бага байгаа тул дэлгэцийн авсан зургийг хадгалах боломжгүй байна."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Дэлгэцээс дарсан зургийг хадгалж байна"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Дэлгэцээс дарсан зургийг хадгалсан"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Дэлгэцээс дарсан зургийг харах бол товшино уу"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Дэлгэцийн зургийг дарж чадсангүй"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Дэлгэцээс дарсан зургийг хадгалахад алдаа гарлаа"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB файл шилжүүлэх сонголт"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Медиа тоглуулагч(MTP) болгон залгах"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Идэвхгүй"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Тэжээлийн мэдэгдлийн удирдлагын тусламжтайгаар та апп-н мэдэгдэлд 0-5 хүртэлх ач холбогдлын түвшин тогтоох боломжтой. \n\n"<b>"5-р түвшин"</b>" \n- Мэдэгдлийн жагсаалтын хамгийн дээр харуулна \n- Бүтэн дэлгэцэд саад болно \n- Дэлгэцэд тогтмол гарч ирнэ \n\n"<b>"4-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд тогтмол гарч ирнэ \n\n"<b>"3-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n\n"<b>"2-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n- Дуу болон чичиргээ хэзээ ч гаргахгүй \n\n"<b>"1-р түвшин"</b>" \n- Бүтэн дэлгэцэд саад болохоос сэргийлнэ \n- Дэлгэцэд хэзээ ч гарч ирэхгүй \n- Дуу болон чичиргээ хэзээ ч гаргахгүй \n- Түгжигдсэн дэлгэц болон статусын самбараас нууна \n- Мэдэгдлийн жагсаалтын доор харуулна \n\n"<b>"0-р түвшин"</b>" \n- Энэ апп-н бүх мэдэгдлийг блоклоно"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Мэдэгдэл"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ta цаашид эдгээр мэдэгдлийг авахгүй"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> мэдэгдлийн ангилал"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Энэ апп-д мэдэгдлийн категори алга"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Энэ аппын мэдэгдлийг унтраах боломжгүй"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Энэ аппын <xliff:g id="NUMBER_1">%s</xliff:g> мэдэгдлийн категорийн 1</item> - <item quantity="one">Энэ аппын <xliff:g id="NUMBER_0">%s</xliff:g> мэдэгдлийн категорийн 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, бусад <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, бусад <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Та эдгээр мэдэгдлийг цаашид харахгүй"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Эдгээр мэдэгдлийг харуулсан хэвээр байх уу?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Мэдэгдлийг зогсоох"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Харуулсан хэвээр байх"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Энэ аппаас мэдэгдэл харуулсан хэвээр байх уу?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Эдгээр мэдэгдлийг унтраах боломжгүй"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг нээсэн"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг хаасан"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Энэ сувгийн мэдэгдлийг зөвшөөрөх"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Бүх ангилал"</string> <string name="notification_more_settings" msgid="816306283396553571">"Бусад тохиргоо"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Өөрчлөх: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Тохируулах"</string> <string name="notification_done" msgid="5279426047273930175">"Дууссан"</string> + <string name="inline_undo" msgid="558916737624706010">"Болих"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"мэдэгдлийн удирдлага"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"мэдэгдэл түр хойшлуулагчийн сонголт"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Дэлгэх"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Багасгах"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Хаах"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Тохиргоо"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Хаахын тулд доош чирэх"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Цэс"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index aaf907eacd19..803366fe2918 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"सुरु असलेले"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचना"</string> <string name="battery_low_title" msgid="6456385927409742437">"बॅटरी कमी आहे"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक. बॅटरी सेव्हर चालू आहे."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB चार्जिंग समर्थित नाही.\nफक्त पुरवठा केलेले चार्जर वापरा."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्जिंग समर्थित नाही."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"सध्या या डीव्हाइसमध्ये साइन इन केलेला वापरकर्ता USB डीबग करणे चालू करू शकत नाही. हे वैशिष्ट्य वापरण्यासाठी, प्राथमिक वापरकर्त्यावर स्विच करा."</string> <string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरण्यासाठी झूम करा"</string> <string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरण्यासाठी ताणा"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रीनशॉट जतन करत आहे…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रीनशॉट जतन करत आहे…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रीनशॉट जतन केला जात आहे."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कॅप्चर केला."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"आपला स्क्रीनशॉट पाहण्यासाठी टॅप करा."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट कॅप्चर करू शकलो नाही."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रीनशॉट जतन करताना समस्या आली."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"मर्यादित संचय जागेमुळे स्क्रीनशॉट जतन करू शकत नाही."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"मीडिया प्लेअर म्हणून माउंट करा (MTP)"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- पूर्ण स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n\n"<b>"स्तर 1"</b>\n"- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा कंपन करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"तुम्हाला यापुढे या सूचना प्राप्त होणार नाहीत"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> सूचना श्रेण्या"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"या अॅपला सूचना श्रेण्या नाहीत"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"या अॅपकडून येणार्या सूचना बंद ठेवता येणार नाहीत"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">या अॅपकडील <xliff:g id="NUMBER_1">%s</xliff:g> पैकी 1 सूचना श्रेणी</item> - <item quantity="other">या अॅपकडील <xliff:g id="NUMBER_1">%s</xliff:g> पैकी 1 सूचना श्रेणी</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, आणि <xliff:g id="NUMBER_5">%3$d</xliff:g> इतर</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, आणि <xliff:g id="NUMBER_5">%3$d</xliff:g> इतर</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे बंद आहेत"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"या चॅनेलकडील सूचनांना मान्यता द्या"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"सर्व श्रेण्या"</string> <string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"सानुकूल करा: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना नियंत्रणे"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना स्नूझ पर्याय"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत करा"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"लहान करा"</string> <string name="pip_phone_close" msgid="8416647892889710330">"बंद करा"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिंग्ज"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"डिसमिस करण्यासाठी खाली ड्रॅग करा"</string> <string name="pip_menu_title" msgid="4707292089961887657">"मेनू"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> चित्रामध्ये चित्र मध्ये आहे"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 118d2c7174b0..399be8b298a1 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sedang berlangsung"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pemberitahuan"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateri lemah"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Bateri lemah. Hidupkan Penjimat Bateri"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> yang tinggal"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi berdasarkan penggunaan anda"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>, kira-kira <xliff:g id="TIME">%s</xliff:g> lagi"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>. Penjimat Bateri dihidupkan."</string> <string name="invalid_charger" msgid="4549105996740522523">"Pengecasan USB tidak disokong.\nGunakan hanya pengecas yang dibekalkan."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Pengecasan USB tidak disokong."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Pengguna yang log masuk ke peranti ini pada masa ini tidak boleh menghidupkan penyahpepijatan USB. Untuk menggunakan ciri ini, tukar kepada pengguna utama."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zum untuk memenuhi skrin"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Regang utk memenuhi skrin"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Tangkapan skrin"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Menyimpan tangkapan skrin..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Menyimpan tangkapan skrin..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Tangkapan skrin sedang disimpan."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Tangkapan skrin ditangkap."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Ketik untuk melihat tangkapan skrin anda."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat menangkap tangkapan skrin."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Berlaku masalah semasa menyimpan tangkapan skrin."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Tangkapan skrin sedang disimpan"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Tangkapan skrin disimpan"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Ketik untuk melihat tangkapan skrin anda"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Tidak dapat menangkap tangkapan skrin"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Masalah berlaku semasa menyimpan tangkapan skrin"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Pilihan pemindahan fail USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Lekapkan sebagai pemain media (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Mati"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Dengan kawalan pemberitahuan berkuasa, anda boleh menetapkan tahap kepentingan dari 0 hingga 5 untuk pemberitahuan apl. \n\n"<b>"Tahap 5"</b>" \n- Tunjukkan pada bahagian atas senarai pemberitahuan \n- Benarkan gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 4"</b>" \n- Halang gangguan skrin penuh \n- Sentiasa intai \n\n"<b>"Tahap 3"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n\n"<b>"Tahap 2"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi dan bergetar \n\n"<b>"Tahap 1"</b>" \n- Halang gangguan skrin penuh \n- Jangan intai \n- Jangan berbunyi atau bergetar \n- Sembunyikan daripada skrin kunci dan bar status \n- Tunjukkan di bahagian bawah senarai pemberitahuan \n\n"<b>"Tahap 0"</b>" \n- Sekat semua pemberitahuan daripada apl"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Pemberitahuan"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Anda tidak akan menerima pemberitahuan ini lagi"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> kategori pemberitahuan"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Apl ini tiada kategori pemberitahuan"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Pemberitahuan daripada apl ini tidak boleh dimatikan"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 daripada <xliff:g id="NUMBER_1">%s</xliff:g> kategori pemberitahuan daripada apl ini</item> - <item quantity="one">1 daripada <xliff:g id="NUMBER_0">%s</xliff:g> kategori pemberitahuan daripada apl ini</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> dan <xliff:g id="NUMBER_5">%3$d</xliff:g> yang lain</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> dan <xliff:g id="NUMBER_2">%3$d</xliff:g> yang lain</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Anda tidak akan melihat pemberitahuan ini lagi"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Terus tunjukkan pemberitahuan ini?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Hentikan pemberitahuan"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Terus tunjukkan"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tunjukkan pemberitahuan daripada apl ini?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Pemberitahuan ini tidak boleh dimatikan"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> ditutup"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Benarkan pemberitahuan daripada saluran ini"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Semua Kategori"</string> <string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Sesuaikan: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Sesuaikan"</string> <string name="notification_done" msgid="5279426047273930175">"Selesai"</string> + <string name="inline_undo" msgid="558916737624706010">"Buat asal"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kawalan pemberitahuan"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"pilihan tunda pemberitahuan"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Kembangkan"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimumkan"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Tutup"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Tetapan"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Seret ke bawah untuk mengetepikan"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index faca35213e34..ac6adb8f5a58 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"လက်ရှိအသုံးပြုမှု"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"အကြောင်းကြားချက်များ။"</string> <string name="battery_low_title" msgid="6456385927409742437">"ဘက်ထရီ အားနည်းနေ"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ဘက်ထရီ အားနည်းနေပါပြီ။ ဘက်ထရီအားထိန်းကို ဖွင့်ပါ"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်ရှိနေ"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သည်၊ သင့်အသုံးပြုမှုအရ <xliff:g id="TIME">%s</xliff:g> ခန့် ကျန်ပါသည်"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သည်၊ <xliff:g id="TIME">%s</xliff:g> ခန့် ကျန်ပါသည်"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်ပါတယ်။ ဘက်ထရီ အားထိန်းကို ဖွင့်ထားသည်။"</string> <string name="invalid_charger" msgid="4549105996740522523">"လက်ရှိUSBအားသွင်းခြင်း အသုံးမပြုနိုင်ပါ \n ပေးထားသောအားသွင်းကိရိယာကိုသာ အသုံးပြုပါ"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB အားသွင်းမှု မပံ့ပိုးပါ။"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ဤစက်ပစ္စည်းသို့ လက်ရှိဝင်ရောက်ထားသည့် အသုံးပြုသူသည် USB အမှားပြင်ဆင်ခြင်းကို ဖွင့်၍မရပါ။ ဤဝန်ဆောင်မှုကို အသုံးပြုရန် အဓိကအသုံးပြုသူအဖြစ်သို့ ပြောင်းပါ။"</string> <string name="compat_mode_on" msgid="6623839244840638213">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ဖန်သားပြင်ဓာတ်ပုံ"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပြီးပါပြီ"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ဖမ်းယူပြီး"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"မျက်နှာပြင်ပုံဖမ်းယူခြင်းကို ကြည့်ရှုရန် တို့ပါ"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား မဖမ်းစီးနိုင်ပါ"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ဖန်သားပြင်ဓာတ်ပုံဖမ်းယူမှုကို သိမ်းဆည်းရာတွင် ပြဿနာကြုံခဲ့သည်။"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ။"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"ဖန်သားပြင်ဓါတ်ပုံကို သိမ်းနေသည်"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းပြီးပါပြီ"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"ဖန်သားပြင်ဓာတ်ပုံကို ကြည့်ရန် တို့ပါ"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"ဖန်သားပြင်ဓါတ်ပုံ ရိုက်၍မရပါ"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းရာတွင် ပြဿနာရှိနေသည်"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"မီဒီယာပလေရာအနေဖြင့် တပ်ဆင်ရန် (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ပိတ်ပါ"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"အကြောင်းကြားချက်များ"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"သင်သည် ဤအကြောင်းကြားချက်များကို နောက်ထပ် လက်ခံရရှိတော့မည် မဟုတ်ပါ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"အကြောင်းကြားချက် အမျိုးအစား <xliff:g id="NUMBER">%d</xliff:g> ခု"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ဤအက်ပ်တွင် အကြောင်းကြားချက် အမျိုးအစားများ မရှိပါ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ဤအက်ပ်မှပို့သော အကြောင်းကြားချက်များကို ပိတ်ထား၍မရပါ"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">ဤအက်ပ်ရှိ အကြောင်းကြားချက်အမျိုးအစား <xliff:g id="NUMBER_1">%s</xliff:g> ခု အနက်မှ ၁ ခု</item> - <item quantity="one">ဤအက်ပ်ရှိ အကြောင်းကြားချက်အမျိုးအစား <xliff:g id="NUMBER_0">%s</xliff:g> ခု အနက်မှ ၁ ခု</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> နှင့် <xliff:g id="NUMBER_5">%3$d</xliff:g> အခြား</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>၊ <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> နှင့် <xliff:g id="NUMBER_2">%3$d</xliff:g> အခြား</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"ဤအကြောင်းကြားချက်များကို မြင်ရတော့မည် မဟုတ်ပါ"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"ဤအကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"အကြောင်းကြားချက်များကို ရပ်ရန်"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"ဆက်ပြရန်"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"ဤအက်ပ်ထံမှ အကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ဤအကြောင်းကြားချက်များကို ပိတ်၍မရပါ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ဖွင့်ထားသည်"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ပိတ်ထားသည်"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ဤချန်နယ်မှ အကြောင်းကြားချက်များကို ခွင့်ပြုပါ"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"အုပ်စုအားလုံး"</string> <string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"စိတ်ကြိုက်သတ်မှတ်ရန်− <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"စိတ်ကြိုက်ပြုလုပ်ရန်"</string> <string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string> + <string name="inline_undo" msgid="558916737624706010">"တစ်ဆင့်နောက်ပြန်ရန်"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"အကြောင်းကြားချက်များကို ဆိုင်းငံ့ရန် ရွေးချယ်စရာများ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index fbcd19742290..0e7447aa8f25 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varsler"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batterikapasiteten er lav"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Du har lite batteri. Slå på batterisparing"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår, omtrent <xliff:g id="TIME">%s</xliff:g> igjen basert på bruken din"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår, omtrent <xliff:g id="TIME">%s</xliff:g> igjen"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> gjenstår. Batterisparing er på."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB-lading støttes ikke.\nBruk kun den medfølgende laderen."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Lading via USB støttes ikke."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Brukeren som for øyeblikket er logget på denne enheten, kan ikke slå på USB-feilsøking. For å bruke denne funksjonen, bytt til hovedbrukeren."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom for å fylle skjermen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Strekk for å fylle skjerm"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skjermdump"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Lagrer skjermdumpen …"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Lagrer skjermdumpen …"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Skjermdumpen lagres."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skjermdumpen er lagret."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Trykk for å se skjermdumpen."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Kan ikke lagre skjermdumpen."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Det oppsto et problem under lagring av skjermdumpen."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Skjermdumpen lagres"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skjermdumpen er lagret"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Trykk for å se skjermdumpen"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Kan ikke lagre skjermdumpen"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Det oppsto et problem under lagring av skjermdumpen"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Altern. for USB-filoverføring"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Sett inn som mediespiller (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Av"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Med effektive varselinnstillinger kan du angi viktighetsnivåer fra 0 til 5 for appvarsler. \n\n"<b>"Nivå 5"</b>" \n– Vis øverst på varsellisten \n– Tillat forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 4"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis alltid raskt \n\n"<b>"Nivå 3"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n\n"<b>"Nivå 2"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri fort \n– Tillat aldri lyder eller vibrering \n\n"<b>"Nivå 1"</b>" \n– Forhindre forstyrrelser ved fullskjermmodus \n– Vis aldri raskt \n– Tillat aldri lyder eller vibrering \n– Skjul fra låseskjermen og statusfeltet \n– Vis nederst på varsellisten \n\n"<b>"Nivå 0"</b>" \n– Blokkér alle varsler fra appen"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Varsler"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Du får ikke disse varslene lenger"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> varselkategorier"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Denne appen har ikke varselkategorier"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Varsler fra denne appen kan ikke slås av"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 av <xliff:g id="NUMBER_1">%s</xliff:g> varselkategorier fra denne appen</item> - <item quantity="one">1 av <xliff:g id="NUMBER_0">%s</xliff:g> varselkategori fra denne appen</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> og <xliff:g id="NUMBER_5">%3$d</xliff:g> andre</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> og <xliff:g id="NUMBER_2">%3$d</xliff:g> annen</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Du ser ikke disse varslene lenger"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Vil du fortsette å vise disse varslene?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stopp varsler"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Fortsett å vise"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsette å vise varsler fra denne appen?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Du kan ikke slå av disse varslene"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er åpnet"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er lukket"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillat varsler fra denne kanalen"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alle kategorier"</string> <string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Tilpass: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Tilpass"</string> <string name="notification_done" msgid="5279426047273930175">"Ferdig"</string> + <string name="inline_undo" msgid="558916737624706010">"Angre"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"varselinnstillinger"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"slumrealternativer for varsler"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Vis"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimer"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Lukk"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Innstillinger"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Dra ned for å avvise"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meny"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index ceda3c5b0f77..5814542d6841 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"चलिरहेको"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाहरू"</string> <string name="battery_low_title" msgid="6456385927409742437">"ब्याट्री कम छ"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"ब्याट्रीको स्तर न्यून छ। ब्याट्री सेभर सक्रिय गर्नुहोस्"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी, तपाईंको प्रयोगका आधारमा करिब <xliff:g id="TIME">%s</xliff:g> बाँकी छ"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी, करिब <xliff:g id="TIME">%s</xliff:g> बाँकी छ"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> बाँकी। ब्याट्री सेभर सक्रिय छ।"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB चार्ज गर्न समर्थित छैन।\n आपूर्ति गरिएको चार्जर मात्र प्रयोग गर्नुहोस्।"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB चार्ज समर्थित छैन।"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"हाल यस यन्त्रमा साइन इन हुनुभएको प्रयोगकर्ताले USB डिबग सक्रिय गर्न सक्नुहुन्न। यो सुविधाको प्रयोग गर्न प्राथमिक प्रयोगकर्तामा बदल्नुहोस्।"</string> <string name="compat_mode_on" msgid="6623839244840638213">"स्क्रिन भर्न जुम गर्नुहोस्"</string> <string name="compat_mode_off" msgid="4434467572461327898">"स्क्रिन भर्न तन्काउनुहोस्"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"स्क्रिनसट"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"स्क्रिनसट बचत गर्दै…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"स्क्रिनसट बचत गर्दै…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"स्क्रिनसट बचत हुँदैछ।"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रिनसट क्याप्चर गरियो।"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"आफ्नो स्क्रिनसट हेर्न ट्याप गर्नुहोस्।"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रिनसट क्याप्चर गर्न सकिएन।"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रिनसटलाई सुरक्षित गर्दा समस्या भयो।"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"सीमित भण्डारण स्थान उपलब्ध रहेको हुनाले स्क्रिनसटलाई सुरक्षित गर्न सकिँदैन।"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"स्क्रिनसट सुरक्षित गरिँदै छ"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"स्क्रिनसट सुरक्षित गरियो"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"आफ्नो स्क्रिनसट हेर्न ट्याप गर्नुहोस्"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"स्क्रिनसट खिच्न सकिएन"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"स्क्रिनसट सुरक्षित गर्ने क्रममा समस्या आइपर्यो"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"उक्त अनुप्रयोग वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB फाइल सार्ने विकल्पहरू"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"मिडिया प्लेयर(MTP)को रूपमा माउन्ट गर्नुहोस्"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"निष्क्रिय"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचनाहरू"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"तपाईंले अब उप्रान्त यी सूचनाहरू प्राप्त गर्नुहुने छैन"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> सूचनाका कोटिहरू"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"यस अनुप्रयोगमा सूचना सम्बन्धी कोटीहरू छैनन्"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"यस अनुप्रयोगका सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">यस अनुप्रयोगका <xliff:g id="NUMBER_1">%s</xliff:g> सूचना कोटिहरू मध्ये १</item> - <item quantity="one"> यस अनुप्रयोगको <xliff:g id="NUMBER_0">%s</xliff:g> सूचना कोटी मध्ये १</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> र <xliff:g id="NUMBER_5">%3$d</xliff:g> अन्य</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> र <xliff:g id="NUMBER_2">%3$d</xliff:g> अन्य</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"तपाईं अब उप्रान्त यी सूचनाहरू देख्नु हुने छैन"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"यी सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"सूचनाहरू देखाउन छाड्नुहोस्"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"देखाउने क्रम जारी राख्नुहोस्"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"यी सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई खोलियो"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई बन्द गरियो"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"यो च्यानलका सूचनाहरूलाई अनुमति दिनुहोस्"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"सबै कोटिहरू"</string> <string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"आफू अनुकूल पार्नुहोस्: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"आफू अनुकूल पार्नुहोस्"</string> <string name="notification_done" msgid="5279426047273930175">"सम्पन्न भयो"</string> + <string name="inline_undo" msgid="558916737624706010">"अन्डू गर्नुहोस्"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"सूचना सम्बन्धी नियन्त्रणहरू"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"सूचना स्नुज गर्ने विकल्पहरू"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"विस्तृत गर्नुहोस्"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"सानो बनाउनुहोस्"</string> <string name="pip_phone_close" msgid="8416647892889710330">"बन्द गर्नुहोस्"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"सेटिङहरू"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"खारेज गर्न तल तान्नुहोस्"</string> <string name="pip_menu_title" msgid="4707292089961887657">"मेनु"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 7139a635413a..55a6cb88510e 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batterij is bijna leeg"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"De batterij is bijna leeg. Schakel Batterijbesparing in."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend, nog ongeveer <xliff:g id="TIME">%s</xliff:g> over op basis van je gebruik"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend, nog ongeveer <xliff:g id="TIME">%s</xliff:g> over"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend. Batterijbesparing is ingeschakeld."</string> <string name="invalid_charger" msgid="4549105996740522523">"Opladen via USB niet ondersteund.\nGebruik alleen de bijgeleverde oplader."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Opladen via USB wordt niet ondersteund."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"De gebruiker die momenteel is ingelogd op dit apparaat, kan USB-foutopsporing niet inschakelen. Als je deze functie wilt gebruiken, schakel je naar de primaire gebruiker."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom om scherm te vullen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Rek uit v. schermvulling"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Screenshot opslaan..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot opslaan..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wordt opgeslagen."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Tik om je screenshot te bekijken."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Er is een probleem opgetreden bij het opslaan van het screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan screenshot niet opslaan vanwege beperkte opslagruimte."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot wordt opgeslagen"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot opgeslagen"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Tik om je screenshot te bekijken"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Kan geen screenshot maken"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Er is een probleem opgetreden bij het opslaan van het screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Koppelen als mediaspeler (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Uit"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Met beheeropties voor meldingen met betrekking tot stroomverbruik kun je een belangrijkheidsniveau van 0 tot 5 instellen voor de meldingen van een app. \n\n"<b>"Niveau 5"</b>" \n- Boven aan de lijst met meldingen weergeven \n- Onderbreking op volledig scherm toestaan \n- Altijd korte weergave \n\n"<b>"Niveau 4"</b>" \n- Geen onderbreking op volledig scherm \n- Altijd korte weergave \n\n"<b>"Niveau 3"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n\n"<b>"Niveau 2"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n\n"<b>"Niveau 1"</b>" \n- Geen onderbreking op volledig scherm \n- Nooit korte weergave \n- Nooit geluid laten horen of trillen \n- Verbergen op vergrendelingsscherm en statusbalk \n- Onder aan de lijst met meldingen weergeven \n\n"<b>"Niveau 0"</b>" \n- Alle meldingen van de app blokkeren"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Meldingen"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Je ontvangt deze meldingen niet meer"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> meldingscategorieën"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Deze app heeft geen meldingscategorieën"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Meldingen van deze app kunnen niet worden uitgeschakeld"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 van <xliff:g id="NUMBER_1">%s</xliff:g> meldingscategorieën van deze app</item> - <item quantity="one">1 van <xliff:g id="NUMBER_0">%s</xliff:g> meldingscategorie van deze app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> en <xliff:g id="NUMBER_5">%3$d</xliff:g> andere</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> en <xliff:g id="NUMBER_2">%3$d</xliff:g> andere</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Deze meldingen worden niet meer weergegeven"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Deze meldingen blijven weergeven?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Meldingen stoppen"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Blijven weergeven"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Meldingen van deze app blijven weergeven?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Deze meldingen kunnen niet worden uitgeschakeld"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> geopend"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> gesloten"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Meldingen van dit kanaal toestaan"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alle categorieën"</string> <string name="notification_more_settings" msgid="816306283396553571">"Meer instellingen"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Aanpassen: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Aanpassen"</string> <string name="notification_done" msgid="5279426047273930175">"Gereed"</string> + <string name="inline_undo" msgid="558916737624706010">"Ongedaan maken"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"beheeropties voor meldingen"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"snooze-opties voor meldingen"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Uitvouwen"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimaliseren"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Sluiten"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Instellingen"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Sleep omlaag om te sluiten"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index b141801953c0..b65ce97a0f3d 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ਜਾਰੀ"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ਸੂਚਨਾਵਾਂ"</string> <string name="battery_low_title" msgid="6456385927409742437">"ਬੈਟਰੀ ਘੱਟ ਹੈ"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ। ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ।"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ਚਾਰਜਿੰਗ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।\nਕੇਵਲ ਸਪਲਾਈ ਕੀਤਾ ਚਾਰਜਰ ਵਰਤੋ।"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ਚਾਰਜਿੰਗ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।"</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕੈਪਚਰ ਕੀਤਾ।"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ਆਪਣਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ।"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕੈਪਚਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕਰਨ ਦੌਰਾਨ ਸਮੱਸਿਆ ਆਈ।"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ਸਟੋਰੇਜ ਦੀ ਸੀਮਿਤ ਜਗ੍ਹਾ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ।"</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਚੋਣਾਂ"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ਇੱਕ ਮੀਡੀਆ ਪਲੇਅਰ (MTP) ਦੇ ਤੌਰ ਤੇ ਮਾਊਂਟ ਕਰੋ"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ਬੰਦ"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ਪਾਵਰ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨਾਲ, ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਦੀਆਂ ਸੂਚਨਾਵਾਂ ਲਈ ਮਹੱਤਤਾ ਪੱਧਰ ਨੂੰ 0 ਤੋਂ 5 ਤੱਕ ਸੈੱਟ ਕਰ ਸਕਦੇ ਹੋ। \n\n"<b>"ਪੱਧਰ 5"</b>" \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਦਿਖਾਓ \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਦੀ ਆਗਿਆ ਦਿਓ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 4"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਹਮੇਸ਼ਾਂ ਝਲਕ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 3"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 2"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਵੀ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n\n"<b>"ਪੱਧਰ 1"</b>" \n- ਪੂਰੀ ਸਕ੍ਰੀਨ ਰੁਕਾਵਟ ਨੂੰ ਰੋਕੋ \n- ਕਦੇ ਝਲਕ ਨਾ ਦਿਖਾਓ \n- ਕਦੇ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਾ ਕਰੋ \n- ਲਾਕ ਸਕ੍ਰੀਨ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਤੋਂ ਲੁਕਾਓ \n- ਸੂਚਨਾ ਸੂਚੀ ਦੇ ਹੇਠਾਂ ਦਿਖਾਓ \n\n"<b>"ਪੱਧਰ 0"</b>" \n- ਐਪ ਤੋਂ ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕਰੋ"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"ਸੂਚਨਾਵਾਂ"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"ਤੁਹਾਨੂੰ ਹੁਣ ਇਹ ਸੂਚਨਾਵਾਂ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੀਆਂ"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀਆਂ"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ਇਸ ਐਪ ਵਿੱਚ ਸੂਚਨਾ ਸ਼੍ਰੇਣੀਆਂ ਨਹੀਂ ਹਨ"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">ਇਸ ਐਪ ਤੋਂ <xliff:g id="NUMBER_1">%s</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀ ਵਿੱਚੋਂ 1</item> - <item quantity="other">ਇਸ ਐਪ ਤੋਂ <xliff:g id="NUMBER_1">%s</xliff:g> ਸੂਚਨਾ ਸ਼੍ਰੇਣੀ ਵਿੱਚੋਂ 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ਅਤੇ <xliff:g id="NUMBER_5">%3$d</xliff:g> ਹੋਰ</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, ਅਤੇ <xliff:g id="NUMBER_5">%3$d</xliff:g> ਹੋਰ</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ਇਸ ਚੈਨਲ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਇਜਾਜ਼ਤ ਦਿਓ"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ਸਭ ਸ਼੍ਰੇਣੀਆਂ"</string> <string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ਵਿਉਂਤਬੱਧ ਕਰੋ: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ਸੂਚਨਾ ਕੰਟਰੋਲ"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ਸੂਚਨਾ ਸਨੂਜ਼ ਵਿਕਲਪ"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ਵਿਸਤਾਰ ਕਰੋ"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ਛੋਟਾ ਕਰੋ"</string> <string name="pip_phone_close" msgid="8416647892889710330">"ਬੰਦ ਕਰੋ"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ਸੈਟਿੰਗਾਂ"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ਖਾਰਜ ਕਰਨ ਲਈ ਹੇਠਾਂ ਘਸੀਟੋ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"ਮੀਨੂ"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ \'ਚ ਹੈ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 4c385b7235f0..0c4b71eafc3f 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Bieżące"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Powiadomienia"</string> <string name="battery_low_title" msgid="6456385927409742437">"Niski poziom baterii"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Niski poziom naładowania baterii. Włącz Oszczędzanie baterii"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g>, jeszcze około <xliff:g id="TIME">%s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g>, jeszcze około <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g>. Oszczędzanie baterii jest włączone."</string> <string name="invalid_charger" msgid="4549105996740522523">"Ładowanie przy użyciu złącza USB nie jest obsługiwane.\nNależy używać tylko dołączonej ładowarki."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ładowanie przez USB nie jest obsługiwane."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Użytkownik obecnie zalogowany na tym urządzeniu nie może włączyć debugowania USB. Aby użyć tej funkcji, przełącz się na użytkownika głównego."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Powiększ, aby wypełnić ekran"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Rozciągnij, aby wypełnić ekran"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Zrzut ekranu"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Zapisywanie zrzutu ekranu..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Zapisywanie zrzutu ekranu..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Zapisywanie zrzutu ekranu."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Wykonano zrzut ekranu."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Kliknij, by zobaczyć zrzut ekranu."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nie udało się wykonać zrzutu ekranu."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Podczas zapisywania zrzutu ekranu wystąpił błąd."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Zapisywanie zrzutu ekranu"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Zrzut ekranu został zapisany"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Kliknij, by zobaczyć zrzut ekranu"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Nie udało się wykonać zrzutu ekranu"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Podczas zapisywania zrzutu ekranu wystąpił błąd"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB – opcje przesyłania plików"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Podłącz jako odtwarzacz multimedialny (MTP)"</string> @@ -561,30 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Wył."</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Dzięki zaawansowanym ustawieniom możesz określić poziom ważności powiadomień z aplikacji w skali od 0 do 5. \n\n"<b>"Poziom 5"</b>" \n– Pokazuj u góry listy powiadomień \n– Zezwalaj na powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 4"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Zawsze pokazuj podgląd \n\n"<b>"Poziom 3"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n\n"<b>"Poziom 2"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n\n"<b>"Poziom 1"</b>" \n– Wyłącz powiadomienia na pełnym ekranie \n– Nigdy nie pokazuj podglądu \n– NIgdy nie powiadamiaj dźwiękiem ani wibracjami \n– Ukrywaj na ekranie blokady i pasku stanu \n– Pokazuj u dołu listy powiadomień \n\n"<b>"Poziom 0"</b>" \n– Blokuj wszystkie powiadomienia aplikacji"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Powiadomienia"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Nie będziesz już otrzymywać tych powiadomień"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Kategorie powiadomień: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ta aplikacja nie ma kategorii powiadomień"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Powiadomień z tej aplikacji nie można wyłączyć"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="few">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorii powiadomień z tej aplikacji</item> - <item quantity="many">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorii powiadomień z tej aplikacji</item> - <item quantity="other">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategorii powiadomień z tej aplikacji</item> - <item quantity="one">1 z <xliff:g id="NUMBER_0">%s</xliff:g> kategorii powiadomień z tej aplikacji</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i <xliff:g id="NUMBER_5">%3$d</xliff:g> inne</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i <xliff:g id="NUMBER_5">%3$d</xliff:g> innych</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> i <xliff:g id="NUMBER_5">%3$d</xliff:g> innego</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> i <xliff:g id="NUMBER_2">%3$d</xliff:g> inny</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Te powiadomienia nie będą już wyświetlane"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Nadal pokazywać te powiadomienia?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Zatrzymaj powiadomienia"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Pokazuj nadal"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Nadal pokazywać powiadomienia z tej aplikacji?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tych powiadomień nie można wyłączyć"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> otwarte"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> zamknięte"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Zezwól na powiadomienia z tego kanału"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Wszystkie kategorie"</string> <string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Dostosuj: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Dostosuj"</string> <string name="notification_done" msgid="5279426047273930175">"Gotowe"</string> + <string name="inline_undo" msgid="558916737624706010">"Cofnij"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"sterowanie powiadomieniami"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opcje odkładania powiadomień"</string> @@ -743,8 +736,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Rozwiń"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizuj"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zamknij"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Ustawienia"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Przeciągnij w dół, by zamknąć"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 82658957a0f2..5fb7b1954689 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está com pouca carga. Ative a Economia de bateria"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restantes"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g> com base no seu uso"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s). A Economia de bateria está ativada."</string> <string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvando captura de tela..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Salvando captura de tela..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"A captura de tela está sendo salva."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver sua captura de tela."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao salvar captura de tela."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"A captura de tela está sendo salva"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de tela salva"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver sua captura de tela"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível fazer a captura de tela"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problema encontrado ao salvar captura de tela"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"O app ou a organização não permitem capturas de tela"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Conectar como media player (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Você deixará de receber essas notificações"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Este app não tem categorias de notificação"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificações deste app não podem ser desativadas"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categoria de notificação deste app</item> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação deste app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outro</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outros</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações desse canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string> <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> + <string name="inline_undo" msgid="558916737624706010">"Desfazer"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificação"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de adiamento de notificação"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Configurações"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arraste para baixo para dispensar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index b676a241b8b1..23a2f9884066 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está fraca. Ativar a Poupança de dados"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Cerca de <xliff:g id="TIME">%s</xliff:g> restante(s) com base na sua utilização."</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. Cerca de <xliff:g id="TIME">%s</xliff:g> restante(s)."</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante. A Poupança de bateria está ativada."</string> <string name="invalid_charger" msgid="4549105996740522523">"Carregamento USB não suportado. \nUtilize apenas o carregador fornecido."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento por USB não é suportado."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O utilizador com sessão iniciada atualmente neste dispositivo não pode ativar a depuração USB. Para utilizar esta funcionalidade, mude para o utilizador principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom para preencher o ecrã"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Esticar p. caber em ec. int."</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"A guardar captura de ecrã..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"A guardar captura de ecrã..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"A guardar captura de ecrã."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de ecrã efetuada"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver a sua captura de ecrã."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter captura de ecrã."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao guardar a captura de ecrã."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"A guardar a captura de ecrã…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de ecrã guardada"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver a captura de ecrã."</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível obter a captura de ecrã."</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Foi encontrado um problema ao guardar a captura de ecrã."</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"A aplicação ou a sua entidade não permitem tirar capturas de ecrã"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opções de transm. de fich. USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Montar como leitor de multimédia (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Com os controlos de notificações do consumo de energia, pode definir um nível de importância de 0 a 5 para as notificações de aplicações. \n\n"<b>"Nível 5"</b>" \n- Mostrar no início da lista de notificações \n- Permitir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre \n\n"<b>"Nível 4"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Aparecer rapidamente sempre\n\n"<b>"Nível 3"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n\n"<b>"Nível 2"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir a interrupção do ecrã inteiro \n- Nunca aparecer rapidamente \n- Nunca tocar nem vibrar \n- Ocultar do ecrã de bloqueio e da barra de estado \n- Mostrar no fim da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações da aplicação"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Deixará de receber estas notificações"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicação não tem categorias de notificação"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Não é possível desativar as notificações desta aplicação"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação desta aplicação</item> - <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e mais <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Nunca mais verá estas notificações."</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Pretende continuar a ver estas notificações?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Parar notificações"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuar a mostrar"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Pretende continuar a ver notificações desta aplicação?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar estas notificações."</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações deste canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string> <string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> + <string name="inline_undo" msgid="558916737624706010">"Anular"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controlos de notificação"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de suspensão de notificações"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Definições"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arrastar para baixo para ignorar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"A aplicação <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 82658957a0f2..5fb7b1954689 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está com pouca carga. Ative a Economia de bateria"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restantes"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g> com base no seu uso"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s). A Economia de bateria está ativada."</string> <string name="invalid_charger" msgid="4549105996740522523">"O carregamento via USB não é suportado.\nUse apenas o carregador fornecido."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"O carregamento via USB não é suportado."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvando captura de tela..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Salvando captura de tela..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"A captura de tela está sendo salva."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Toque para ver sua captura de tela."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao salvar captura de tela."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"A captura de tela está sendo salva"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de tela salva"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver sua captura de tela"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível fazer a captura de tela"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problema encontrado ao salvar captura de tela"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"O app ou a organização não permitem capturas de tela"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Conectar como media player (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Desativado"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Com controles de ativação de notificações, é possível definir o nível de importância de 0 a 5 para as notificações de um app. \n\n"<b>"Nível 5"</b>" \n- Exibir na parte superior da lista de notificações \n- Permitir interrupção em tela cheia \n- Sempre exibir \n\n"<b>"Nível 4"</b>" \n- Impedir interrupções em tela cheia \n- Sempre exibir \n\n"<b>"Nível 3"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n\n"<b>"Nível 2"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n\n"<b>"Nível 1"</b>" \n- Impedir interrupções em tela cheia \n- Nunca exibir \n- Nunca emitir som ou vibrar \n- Ocultar da tela de bloqueio e barra de status \n- Exibir na parte inferior da lista de notificações \n\n"<b>"Nível 0"</b>" \n- Bloquear todas as notificações do app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificações"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Você deixará de receber essas notificações"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorias de notificação"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Este app não tem categorias de notificação"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificações deste app não podem ser desativadas"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categoria de notificação deste app</item> - <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação deste app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outro</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e <xliff:g id="NUMBER_5">%3$d</xliff:g> outros</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Você deixará de ver essas notificações"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Continuar mostrando essas notificações?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Bloquear notificações"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Não é possível desativar essas notificações"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permitir notificações desse canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Todas as categorias"</string> <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizar: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizar"</string> <string name="notification_done" msgid="5279426047273930175">"Concluído"</string> + <string name="inline_undo" msgid="558916737624706010">"Desfazer"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificação"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de adiamento de notificação"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Expandir"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizar"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Fechar"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Configurações"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Arraste para baixo para dispensar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 8168dfa1654a..f30f6d3d789d 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"În desfășurare"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificări"</string> <string name="battery_low_title" msgid="6456385927409742437">"Bateria este aproape descărcată"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Bateria este aproape descărcată. Activați Economisirea bateriei."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Procent rămas din baterie <xliff:g id="PERCENTAGE">%s</xliff:g>, în baza utilizării, timp aproximativ rămas <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Procent rămas din baterie <xliff:g id="PERCENTAGE">%s</xliff:g>, timp aproximativ rămas <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>. Economisirea bateriei este activată."</string> <string name="invalid_charger" msgid="4549105996740522523">"Încărcarea USB nu este acceptată. \nUtilizați numai încărcătorul furnizat."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Încărcarea prin USB nu este acceptată."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comutați la utilizatorul principal."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zoom pt. a umple ecranul"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Înt. pt. a umple ecranul"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Captură de ecran"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Se salv. captura de ecran..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Se salvează captura de ecran..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Captura de ecran este salvată."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Captură de ecran salvată."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Atingeți pentru a vedea captura de ecran."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Captura de ecran nu a putut fi realizată."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problemă întâmpinată la salvarea capturii de ecran."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Captura de ecran este în curs de salvare"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Captură de ecran salvată"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Atingeți pentru a vedea captura de ecran"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Captura de ecran nu a putut fi realizată"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problemă întâmpinată la salvarea capturii de ecran"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opțiuni pentru transferul de fișiere prin USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Montați ca player media (MTP)"</string> @@ -561,28 +565,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Dezactivate"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Notificări"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Nu veți mai primi aceste notificări"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> categorii de notificări"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Această aplicație nu are categorii de notificare"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Notificările din această aplicație nu pot fi dezactivate"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="few">1 din <xliff:g id="NUMBER_1">%s</xliff:g> categorii de notificare din această aplicație</item> - <item quantity="other">1 din <xliff:g id="NUMBER_1">%s</xliff:g> de categorii de notificare din această aplicație</item> - <item quantity="one">1 din <xliff:g id="NUMBER_0">%s</xliff:g> categorie de notificare din această aplicație</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, și încă <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Nu veți mai vedea aceste notificări"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Doriți să continuați afișarea acestor notificări?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Opriți notificările"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Continuați afișarea"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Doriți să continuați afișarea notificărilor de la această aplicație?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Aceste notificări nu pot fi dezactivate"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> nu sunt afișate"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Permiteți notificările de la acest canal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Toate categoriile"</string> <string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Personalizați: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizați"</string> <string name="notification_done" msgid="5279426047273930175">"Terminat"</string> + <string name="inline_undo" msgid="558916737624706010">"Anulați"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"comenzile notificării"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opțiuni de amânare a notificării"</string> @@ -739,8 +734,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Extindeți"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizați"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Închideți"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Setări"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Trageți în jos pentru a închide"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meniu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 94566155f05e..4be72ed842b9 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string> <string name="battery_low_title" msgid="6456385927409742437">"Батарея почти разряжена"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батарея почти разряжена. Включите режим энергосбережения."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Осталось: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Заряд батареи – <xliff:g id="PERCENTAGE">%s</xliff:g>, осталось примерно <xliff:g id="TIME">%s</xliff:g> при текущем уровне использования."</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Заряд батареи – <xliff:g id="PERCENTAGE">%s</xliff:g>, осталось примерно <xliff:g id="TIME">%s</xliff:g>."</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Уровень заряда батареи: <xliff:g id="PERCENTAGE">%s</xliff:g>. Включен режим энергосбережения."</string> <string name="invalid_charger" msgid="4549105996740522523">"Зарядка через порт USB не поддерживается.\nИспользуйте только зарядное устройство из комплекта поставки."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Зарядка через USB не поддерживается."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"В этом аккаунте нельзя включить отладку по USB. Перейдите в аккаунт основного пользователя."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Подогнать по размерам экрана"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Растянуть на весь экран"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Сохранение..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Сохранение..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Сохранение..."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сохранен"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Нажмите, чтобы увидеть скриншот"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Не удалось сохранить скриншот."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Не удалось сохранить скриншот."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не удалось сохранить скриншот: недостаточно места."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Сохранение…"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Скриншот сохранен"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Нажмите, чтобы увидеть скриншот."</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ошибка при сохранении скриншота"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Не удалось сохранить скриншот."</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Не удалось сохранить скриншот: недостаточно места."</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string> <string name="usb_preference_title" msgid="6551050377388882787">"Параметры передачи через USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Подключить как мультимедийный проигрыватель (MTP)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Отключено"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"С помощью этой функции вы можете устанавливать уровень важности уведомлений от 0 до 5 для каждого приложения.\n\n"<b>"Уровень 5"</b>\n"‒ Помещать уведомления в начало списка.\n‒ Показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 4\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Показывать всплывающие уведомления.\nУровень 3\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\nУровень 2\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\nУровень 1\n"<b></b>\n"‒ Не показывать полноэкранные уведомления.\n‒ Не показывать всплывающие уведомления.\n‒ Не использовать звук и вибрацию.\n‒ Не показывать на экране блокировки и в строке состояния.\n‒ Помещать уведомления в конец списка.\nУровень 0\n"<b></b>\n"‒ Блокировать все уведомления приложения."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Уведомления"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Вы больше не будете получать эти уведомления"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Категорий уведомлений: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"В приложении нет категорий уведомлений"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Уведомления этого приложения нельзя отключить"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категории уведомлений этого приложения</item> - <item quantity="few">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категорий уведомлений этого приложения</item> - <item quantity="many">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категорий уведомлений этого приложения</item> - <item quantity="other">1 из <xliff:g id="NUMBER_1">%s</xliff:g> категории уведомлений этого приложения</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канал</item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канала</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> каналов</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и ещё <xliff:g id="NUMBER_5">%3$d</xliff:g> канала</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Вы больше не будете получать эти уведомления."</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Показывать эти уведомления?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Отключить уведомления"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Показывать"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Показывать уведомления от этого приложения?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Эти уведомления нельзя отключить."</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> открыты"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> закрыты"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Показывать уведомления с этого канала"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Все категории"</string> <string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>: настроить"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Настроить"</string> <string name="notification_done" msgid="5279426047273930175">"Готово"</string> + <string name="inline_undo" msgid="558916737624706010">"Отменить"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g>: <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"настройки уведомлений"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"параметры отсрочки уведомлений"</string> @@ -745,8 +738,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Развернуть"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Свернуть"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Закрыть"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Настройки"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Чтобы закрыть, потяните вниз"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> находится в режиме \"Картинка в картинке\""</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 338b5226c180..f072b73e17f3 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"දැනට පවතින"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"දැනුම්දීම්"</string> <string name="battery_low_title" msgid="6456385927409742437">"බැටරිය අඩුය"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"බැටරිය අඩුයි. බැටරි සුරැකුම ක්රියාත්මක කරන්න"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව තිබේ"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත, ඔබගේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%s</xliff:g> පමණ"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත, <xliff:g id="TIME">%s</xliff:g> පමණ ඇත"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ඉතිරිව ඇත. බැටරි සුරැකුම ක්රියාත්මකයි."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ආරෝපණය සහය නොදක්වයි.\nසපයන ලද ආරෝපකය පමණක් භාවිතා කරන්න."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ආරෝපණය කිරීම සහාය නොදක්වයි."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"දැනට මෙම උපාංගයට පුරා ඇති පරිශීලකයාට USB නිදොස්කරණය ක්රියාත්මක කළ නොහැක. මෙම විශේෂාංගය භාවිතා කිරීම සඳහා, මූලික පරිශීලකයා වෙත මාරු වෙන්න."</string> <string name="compat_mode_on" msgid="6623839244840638213">"තිරය පිරවීමට විශාලනය කරන්න"</string> <string name="compat_mode_off" msgid="4434467572461327898">"තිරය පිරවීමට අදින්න"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"තිර රුව"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"තිර රුව සුරකිමින්…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"තිර රුව සුරැකෙමින් පවතී…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"තිර රුව සුරැකෙමින් පවතී."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"තිර රුව ග්රහණය කරන ලදි."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ඔබගේ තිර රුව බැලීමට තට්ටු කරන්න."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"තිර රුව ග්රහණය කිරීමට නොහැකි විය."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"තිර රුව සුරකින අතරතුර ගැටලුවක් ඇති විය."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"තිර රුව සුරකිමින් පවතී"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"තිර රුව සුරකින ලදී"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"ඔබගේ තිර රුව බැලීමට තට්ටු කරන්න"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"තිර රුව ග්රහණය කිරීමට නොහැකි විය"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"තිර රුව සුරකින අතරතුර ගැටලුවක් ඇති විය"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ගොනු හුවමාරු විකල්ප"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"මධ්ය ධාවකයක් (MTP) ලෙස සවි කරන්න"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ක්රියාවිරහිතයි"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"බල දැනුම්දීම් පාලන සමගින්, ඔබට යෙදුමක දැනුම්දීම් සඳහා වැදගත්කම 0 සිට 5 දක්වා සැකසිය හැකිය. \n\n"<b>"5 මට්ටම"</b>" \n- දැනුම්දීම් ලැයිස්තුවේ ඉහළින්ම පෙන්වන්න \n- පූර්ණ තිර බාධාවට ඉඩ දෙන්න \n- සැම විට එබී බලන්න \n\n"<b>"4 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- සැම විට එබී බලන්න \n\n"<b>"3 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n\n"<b>"2 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n\n"<b>"1 මට්ටම"</b>" \n- පූර්ණ තිර බාධාව වළක්වන්න \n- කිසි විටක එබී නොබලන්න \n- කිසි විටක හඬ සහ කම්පනය සිදු නොකරන්න \n- අගුලු තිරය සහ තත්ත්ව තීරුව වෙතින් සඟවන්න \n- දැනුම්දීම් ලැයිස්තුවේ පහළින්ම පෙන්වන්න \n\n"<b>"0 මට්ටම"</b>" \n- යෙදුම වෙතින් වන සියලු දැනුම් දීම් සඟවන්න."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"දැනුම් දීම්"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොලැබෙනු ඇත"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"දැනුම්දීම් කාණ්ඩ <xliff:g id="NUMBER">%d</xliff:g>ක්"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"මෙම යෙදුම හට දැනුම්දීම් ප්රවර්ග නොමැත"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"මෙම යෙදුම වෙතින් වන දැනුම්දීම් ක්රියාවිරහිත කළ නොහැකිය"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">මෙම යෙදුමෙන් දැනුම්දීම් ප්රවර්ග <xliff:g id="NUMBER_1">%s</xliff:g>න් 1ක්</item> - <item quantity="other">මෙම යෙදුමෙන් දැනුම්දීම් ප්රවර්ග <xliff:g id="NUMBER_1">%s</xliff:g>න් 1ක්</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, සහ තවත් <xliff:g id="NUMBER_5">%3$d</xliff:g>ක්</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, සහ තවත් <xliff:g id="NUMBER_5">%3$d</xliff:g>ක්</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"ඔබට තවදුරටත් මෙම දැනුම්දීම් නොදකිනු ඇත"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"මෙම දැනුම්දීම් පෙන්වමින් තබන්නද?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"දැනුම්දීම් නවත්වන්න"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"පෙන්වමින් තබන්න"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"මෙම යෙදුම වෙතින් දැනුම්දීම් පෙන්වමින් තබන්නද?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"මෙම දැනුම්දීම් ක්රියාවිරහිත කළ නොහැකිය"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන විවෘත කරන ලදී"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන වසන ලදී"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"මෙම නාලිකාව වෙතින් දැනුම්දීම් සඳහා ඉඩ දෙන්න"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"සියලු ප්රවර්ග"</string> <string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"අභිමත කරන්න: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"අභිරුචිකරණය"</string> <string name="notification_done" msgid="5279426047273930175">"නිමයි"</string> + <string name="inline_undo" msgid="558916737624706010">"පසුගමනය කරන්න"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"දැනුම්දීම් පාලන"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"දැනුම්දීම් මදක් නතර කිරීමේ විකල්ප"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 49415a5854ef..618c95013af9 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Prebiehajúce"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Upozornenia"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batéria je takmer vybitá"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batéria je takmer vybitá. Zapnite Šetrič batérie."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>, približne <xliff:g id="TIME">%s</xliff:g> v závislosti od intenzity využitia"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g>, približne <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Zostáva: <xliff:g id="PERCENTAGE">%s</xliff:g>. Šetrič batérie je zapnutý."</string> <string name="invalid_charger" msgid="4549105996740522523">"Nabíjanie pomocou rozhrania USB nie je podporované.\nPoužívajte iba nabíjačku, ktorá bola dodaná spolu so zariadením."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Nabíjanie prostredníctvom USB nie je podporované."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Používateľ, ktorý je práve prihlásený v tomto zariadení, nemôže zapnúť ladenie USB. Ak chcete použiť túto funkciu, prepnite na hlavného používateľa."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Priblížiť na celú obrazovku"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Na celú obrazovku"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Snímka obrazovky"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Prebieha ukladanie snímky obrazovky..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Prebieha ukladanie snímky obrazovky..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Snímka obrazovky sa ukladá."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Snímka obrazovky bola zaznamenaná."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Klepnutím zobrazíte snímku obrazovky."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Snímku obrazovky sa nepodarilo zachytiť."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Pri ukladaní snímky obrazovky sa vyskytol problém."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snímku obrazovky nie je možné vytvoriť z dôvodu nedostatku miesta v úložisku."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Ukladá sa snímka obrazovky"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Snímka obrazovky bola uložená"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Klepnutím zobrazíte snímku obrazovky"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Snímku obrazovky sa nepodarilo zachytiť"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Pri ukladaní snímky obrazovky sa vyskytol problém"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosu súborov USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Pripojiť ako prehrávač médií (MTP)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Vypnuté"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Pomocou ovládacích prvkov zobrazovania upozornení môžete nastaviť pre upozornenia aplikácie úroveň dôležitosti od 0 do 5. \n\n"<b>"Úroveň 5"</b>" \n– Zobrazovať v hornej časti zoznamu upozornení. \n– Povoliť prerušenia na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 4"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Vždy zobrazovať čiastočne. \n\n"<b>"Úroveň 3"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n\n"<b>"Úroveň 2"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n\n"<b>"Úroveň 1"</b>" \n– Zabrániť prerušeniam na celú obrazovku. \n– Nikdy nezobrazovať čiastočne. \n– Nikdy nespúšťať zvuk ani vibrácie. \n– Skryť na uzamknutej obrazovke a v stavovom riadku. \n– Zobraziť v dolnej časti zoznamu upozornení. \n\n"<b>"Úroveň 0"</b>" \n– Blokovať všetky upozornenia z aplikácie."</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Upozornenia"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Tieto upozornenia už nebudete dostávať"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Počet kategórií upozornení: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Táto aplikácia nemá kategórie upozornení"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Upozornenia z tejto aplikácie sa nedajú vypnúť"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="few">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategórií upozornení z tejto aplikácie</item> - <item quantity="many">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategórie upozornení z tejto aplikácie</item> - <item quantity="other">1 z <xliff:g id="NUMBER_1">%s</xliff:g> kategórií upozornení z tejto aplikácie</item> - <item quantity="one">1 z <xliff:g id="NUMBER_0">%s</xliff:g> kategórie upozornení z tejto aplikácie</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> ďalšie</item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> ďalšieho</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> a <xliff:g id="NUMBER_5">%3$d</xliff:g> ďalších</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> a <xliff:g id="NUMBER_2">%3$d</xliff:g> ďalší</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Tieto upozornenia sa už nebudú zobrazovať"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Majú sa tieto upozornenia naďalej zobrazovať?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Prestať zobrazovať upozornenia"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Naďalej zobrazovať"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Majú sa upozornenia z tejto aplikácie naďalej zobrazovať?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Tieto upozornenia sa nedajú vypnúť"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je otvorené"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je zatvorené"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Povoliť upozornenia z tohto kanála"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Všetky kategórie"</string> <string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Prispôsobiť: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Prispôsobiť"</string> <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string> + <string name="inline_undo" msgid="558916737624706010">"Späť"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ovládacie prvky pre upozornenia"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"možnosti stlmenia upozornení"</string> @@ -745,8 +738,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Rozbaliť"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimalizovať"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zavrieť"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Nastavenia"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zrušíte presunutím nadol"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Ponuka"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 5ba1bb5b55d4..e52a43a0636e 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Trenutno"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obvestila"</string> <string name="battery_low_title" msgid="6456385927409742437">"Akumulator je skoraj izpraznjen"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Akumulator je skoraj izpraznjen. Vklopite varčevanje z energijo akumulatorja"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>, glede na način uporabe imate na voljo še približno <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>, na voljo imate še približno <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Še <xliff:g id="PERCENTAGE">%s</xliff:g>. Vklopljeno je varčevanje z energijo akumulatorja."</string> <string name="invalid_charger" msgid="4549105996740522523">"Polnjenje po povezavi USB ni podprto.\nUporabite priloženi polnilnik."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Polnjenje prek USB-ja ni podprto."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uporabnik, trenutno prijavljen v napravo, ne more vklopiti odpravljanja napak s povezavo USB. Če želite uporabljati to funkcijo, preklopite na primarnega uporabnika."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Povečava čez cel zaslon"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Raztegnitev čez zaslon"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Posnetek zaslona"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Shranjev. posnetka zaslona ..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Shranjevanje posnetka zaslona ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Shranjevanje posnetka zaslona."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Posnetek zaslona je shranjen."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Dotaknite se, če si želite ogledati posnetek zaslona."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Posnetka zaslona ni bilo mogoče shraniti."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Pri shranjevanju posnetka zaslona je prišlo do težave."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Shranjevanje posnetka zaslona"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Posnetek zaslona je shranjen"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Dotaknite se, če si želite ogledati posnetek zaslona"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Posnetka zaslona ni bilo mogoče shraniti"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Pri shranjevanju posnetka zaslona je prišlo do težave"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosa datotek prek USB-ja"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Vpni kot predvajalnik (MTP)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Izklopljeno"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"S kontrolniki za pomebnost obvestila je mogoče za obvestila aplikacije nastaviti stopnjo pomembnosti od 0 do 5. \n\n"<b>"Stopnja 5"</b>" \n– Prikaz na vrhu seznama obvestil \n– Omogočanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 4"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Vedno prikaži hitre predoglede \n\n"<b>"Stopnja 3"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n\n"<b>"Stopnja 2"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n\n"<b>"Stopnja 1"</b>" \n– Preprečevanje prekinitev v celozaslonskem načinu \n– Nikoli ne prikaži hitrih predogledov \n– Nikoli ne uporabi zvoka in vibriranja \n– Skrivanje na zaklenjenem zaslonu in v vrstici stanja \n– Prikaz na dnu seznama obvestil \n\n"<b>"Stopnja 0"</b>" \n– Blokiranje vseh obvestil aplikacije"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Obvestila"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Teh obvestil ne boste več prejemali"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Št. kategorij obvestil: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ta aplikacija nima kategorij obvestil"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Obvestil te aplikacije ni mogoče izklopiti"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorije obvestil iz te aplikacije</item> - <item quantity="two">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item> - <item quantity="few">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item> - <item quantity="other">1 od <xliff:g id="NUMBER_1">%s</xliff:g> kategorij obvestil iz te aplikacije</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drug</item> - <item quantity="two"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> druga</item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drugi</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> in <xliff:g id="NUMBER_5">%3$d</xliff:g> drugih</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ta obvestila ne bodo več prikazana"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Želite, da so ta obvestila še naprej prikazana?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Ustavi prikazovanje obvestil"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Prikazuj še naprej"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite, da so obvestila te aplikacije še naprej prikazana?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Teh obvestil ni mogoče izklopiti"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so odprti"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so zaprti"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Dovoli obvestila iz tega kanala"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Vse kategorije"</string> <string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Prilagodi: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Prilagodi"</string> <string name="notification_done" msgid="5279426047273930175">"Dokončano"</string> + <string name="inline_undo" msgid="558916737624706010">"Razveljavi"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrolniki obvestil"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"možnosti preložitve obvestil"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 2d1591f0f0ed..61f1c86ccba1 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Në vazhdim"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Njoftimet"</string> <string name="battery_low_title" msgid="6456385927409742437">"Niveli i baterisë është i ulët"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Niveli i baterisë është i ulët. Aktivizo \"Kursyesin e baterisë\""</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Ka mbetur edhe <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> të mbetura, rreth <xliff:g id="TIME">%s</xliff:g> të mbetura bazuar në përdorimin tënd"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> të mbetura, rreth <xliff:g id="TIME">%s</xliff:g> të mbetura"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Ka mbetur edhe <xliff:g id="PERCENTAGE">%s</xliff:g>. \"Kursyesi i baterisë\" është i aktivizuar."</string> <string name="invalid_charger" msgid="4549105996740522523">"Ngarkuesi USB nuk mbështetet.\nPërdor vetëm ngarkuesin e dhënë."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ngarkimi i USB-së nuk mbështetet."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin e USB-së. Për ta përdorur këtë funksion, kalo te përdoruesi parësor."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zmadho për të mbushur ekranin"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Shtrije për të mbushur ekranin"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Pamja e ekranit"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Po ruan pamjen e ekranit..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Po ruan pamjen e ekranit…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Pamja e ekranit po ruhet."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Pamja e ekranit u kap."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Trokit për të parë pamjen e ekranit."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Nuk mundi të kapte pamjen e ekranit."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"U has problem gjatë ruajtjes së pamjes së ekranit."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Pamja e ekranit po ruhet"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Pamja e ekranit u ruajt"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Trokit për të parë pamjen e ekranit"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Pamja e ekranit nuk mund të regjistrohej"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"U ndesh një problem gjatë ruajtjes së pamjes së ekranit"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opsionet e transferimit të dosjeve të USB-së"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Lidh si një lexues \"media\" (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Joaktiv"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Me kontrollet e njoftimit të energjisë, mund të caktosh një nivel rëndësie nga 0 në 5 për njoftimet e një aplikacioni. \n\n"<b>"Niveli 5"</b>" \n- Shfaq në krye të listës së njoftimeve \n- Lejo ndërprerjen e ekranit të plotë \n- Gjithmonë shfaq shpejt \n\n"<b>"Niveli 4"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Gijthmonë shfaq shpejt \n\n"<b>"Niveli 3"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n\n"<b>"Niveli 2"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull dhe dridhje \n\n"<b>"Niveli 1"</b>" \n- Parandalo ndërprerjen e ekranit të plotë \n- Asnjëherë mos shfaq shpejt \n- Asnjëherë mos lësho tingull ose dridhje \n- Fshih nga ekrani i kyçjes dhe shiriti i statusit \n- Shfaq në fund të listës së njoftimeve \n\n"<b>"Niveli 0"</b>" \n- Blloko të gjitha njoftimet nga aplikacioni"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Njoftime"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Këto njoftime nuk do t\'i marrësh më"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> kategori njoftimesh"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ky aplikacion nuk ka kategori njoftimesh"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Njoftimet nga ky aplikacion nuk mund të çaktivizohen"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 nga <xliff:g id="NUMBER_1">%s</xliff:g> kategori njoftimi nga ky aplikacion</item> - <item quantity="one">1 nga <xliff:g id="NUMBER_0">%s</xliff:g> kategori njoftimi nga ky aplikacion</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> dhe <xliff:g id="NUMBER_5">%3$d</xliff:g> të tjerë</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> dhe <xliff:g id="NUMBER_2">%3$d</xliff:g> të tjerë</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Nuk do t\'i shikosh më këto njoftime"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Do të vazhdosh t\'i shfaqësh këto njoftime?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Ndalo njoftimet"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Vazhdo të shfaqësh"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Do të vazhdosh t\'i shfaqësh njoftimet nga ky aplikacion?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Këto njoftime nuk mund të çaktivizohen"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë hapur"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë mbyllur"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Lejo njoftimet nga ky kanal"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Të gjitha kategoritë"</string> <string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Peresonalizoje: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Personalizo"</string> <string name="notification_done" msgid="5279426047273930175">"U krye"</string> + <string name="inline_undo" msgid="558916737624706010">"Zhbëj"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"kontrollet e njoftimit"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opsionet e shtyrjes së njoftimit"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Zgjero"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimizo"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Mbyll"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Cilësimet"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Zvarrit poshtë për të larguar"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menyja"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 99d8008fa334..7eb86c2f8d23 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -34,7 +34,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текуће"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Обавештења"</string> <string name="battery_low_title" msgid="6456385927409742437">"Ниво напуњености батерије је низак"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Батерија је скоро празна. Укључите Уштеду батерије"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Још <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Још <xliff:g id="PERCENTAGE">%s</xliff:g>, на основу коришћења остало је око <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Још <xliff:g id="PERCENTAGE">%s</xliff:g>, остало је око <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Још <xliff:g id="PERCENTAGE">%s</xliff:g>. Уштеда батерије је укључена."</string> <string name="invalid_charger" msgid="4549105996740522523">"Пуњење преко USB-а није подржано.\nКористите само приложени пуњач."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Пуњење преко USB-а није подржано."</string> @@ -68,14 +71,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Корисник који је тренутно пријављен на овај уређај не може да укључи отклањање грешака на USB-у. Да бисте користили ову функцију, пребаците на примарног корисника."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Зумирај на целом екрану"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Развуци на цео екран"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Снимак екрана"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Чување снимка екрана..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Чување снимка екрана..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Снимак екрана се чува."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Снимак екрана је направљен."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Додирните да бисте видели снимак екрана."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Није могуће направити снимак екрана."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Дошло је до проблема при чувању снимка екрана."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Чување снимка екрана није успело због ограниченог меморијског простора."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Снимак екрана се чува"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Снимак екрана је сачуван"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Додирните да бисте видели снимак екрана"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Не можете да направите снимак екрана"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Дошло је до проблема при чувању снимка екрана"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Чување снимка екрана није успело због ограниченог меморијског простора"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Апликација или организација не дозвољавају прављење снимака екрана"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Опције USB преноса датотека"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Прикључи као медија плејер (MTP)"</string> @@ -559,28 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Искључено"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Помоћу напредних контрола за обавештења можете да подесите ниво важности од 0. до 5. за обавештења апликације. \n\n"<b>"5. ниво"</b>" \n– Приказују се у врху листе обавештења \n- Дозволи прекид режима целог екрана \n– Увек завируј \n\n"<b>"4. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Увек завируј \n\n"<b>"3. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n\n"<b>"2. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n\n"<b>"1. ниво"</b>" \n– Спречи прекид режима целог екрана \n– Никада не завируј \n– Никада не производи звук или вибрацију \n– Сакриј на закључаном екрану и статусној траци \n– Приказују се у дну листе обавештења \n\n"<b>"0. ниво"</b>" \n– Блокирај сва обавештења из апликације"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Обавештења"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Више нећете добијати ова обавештења"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Категорија обавештења: <xliff:g id="NUMBER">%d</xliff:g>"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ова апликација нема категорије обавештења"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Обавештења из ове апликације не могу да се искључе"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 од <xliff:g id="NUMBER_1">%s</xliff:g> категорије обавештења за ову апликацију</item> - <item quantity="few">1 од <xliff:g id="NUMBER_1">%s</xliff:g> категорије обавештења за ову апликацију</item> - <item quantity="other">1 од <xliff:g id="NUMBER_1">%s</xliff:g> категорија обавештења за ову апликацију</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и још <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и још <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> и још <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Више нећете видети ова обавештења"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Желите ли да се ова обавештења и даље приказују?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Престани да приказујеш обавештења"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Настави да приказујеш"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Желите ли да се обавештења из ове апликације и даље приказују?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Не можете да искључите ова обавештења"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Контроле обавештења за отварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Контроле обавештења за затварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Дозволи обавештења са овог канала"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Све категорије"</string> <string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Прилагодите: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Прилагоди"</string> <string name="notification_done" msgid="5279426047273930175">"Готово"</string> + <string name="inline_undo" msgid="558916737624706010">"Опозови"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"контроле обавештења"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"опције за одлагање обавештења"</string> @@ -737,8 +732,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Прошири"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Умањи"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Затвори"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Подешавања"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Превуците надоле да бисте одбили"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Мени"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 2d267dfed26c..d2da3b7c5986 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelanden"</string> <string name="battery_low_title" msgid="6456385927409742437">"Lågt batteri"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batterinivån är låg. Aktivera batterisparläge"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> kvar"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> återstår, cirka <xliff:g id="TIME">%s</xliff:g> kvar utifrån din användning"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> återstår, cirka <xliff:g id="TIME">%s</xliff:g> kvar"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> kvar. Batterisparläget är aktiverat."</string> <string name="invalid_charger" msgid="4549105996740522523">"Det går inte att ladda via USB.\nAnvänd endast den laddare som levererades med telefonen."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Det finns inget stöd för laddning via USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Användaren som är inloggad på enheten för närvarande kan inte aktivera USB-felsökning. Byt till den primära användaren om du vill använda den här funktionen."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Zooma för att fylla skärm"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Dra för att fylla skärmen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skärmdump"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Skärmdumpen sparas ..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Skärmdumpen sparas ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Skärmdumpen sparas."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skärmdumpen har tagits."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Visa skärmdumpen genom att trycka här."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Det gick inte att ta någon skärmdump."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Det gick inte att spara skärmdumpen."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Skärmdumpen sparas"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skärmdumpen har sparats"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Visa skärmdumpen genom att trycka här"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Det gick inte att ta en skärmdump"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Det gick inte att spara skärmdumpen"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Överföringsalternativ"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Montera som mediaspelare (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"På"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Med aviseringsinställningarna kan du ange prioritetsnivå från 0 till 5 för aviseringar från en app. \n\n"<b>"Nivå 5"</b>" \n– Visa högst upp i aviseringslistan\n– Tillåt avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 4"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa alltid \n\n"<b>"Nivå 3"</b>" \n- Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n\n"<b>"Nivå 2"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n\n"<b>"Nivå 1"</b>" \n– Tillåt inte avbrott i helskärmsläge \n– Snabbvisa aldrig \n– Aldrig med ljud eller vibration \n– Visa inte på låsskärmen och i statusfältet \n– Visa längst ned i aviseringslistan \n\n"<b>"Nivå 0"</b>" \n– Blockera alla aviseringar från appen"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Aviseringar"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Inga fler aviseringar av det här slaget visas"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> aviseringskategorier"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Det finns inga aviseringskategorier i appen"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Det går inte att inaktivera aviseringar från den här appen"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 av <xliff:g id="NUMBER_1">%s</xliff:g> aviseringskategorier från denna app</item> - <item quantity="one">1 av <xliff:g id="NUMBER_0">%s</xliff:g> aviseringskategorier från denna app</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> och <xliff:g id="NUMBER_5">%3$d</xliff:g> till</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> och <xliff:g id="NUMBER_2">%3$d</xliff:g> till</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"De här aviseringarna visas inte längre"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Vill du fortsätta visa de här aviseringarna?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Stoppa aviseringar"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Fortsätt visa"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Vill du fortsätta visa aviseringar för den här appen?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"De här aviseringarna kan inte inaktiveras"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> är öppna"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> har stängts"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Tillåt aviseringar från den här kanalen"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Alla kategorier"</string> <string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Anpassa: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Anpassa"</string> <string name="notification_done" msgid="5279426047273930175">"Klar"</string> + <string name="inline_undo" msgid="558916737624706010">"Ångra"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"inställningar för aviseringar"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"alternativ för att snooza aviseringar"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Utöka"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimera"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Stäng"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Inställningar"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Tryck och dra nedåt för att avvisa"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Meny"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 71285ad991b0..8cae1c43d72d 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Inaendelea"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Arifa"</string> <string name="battery_low_title" msgid="6456385927409742437">"Betri inaisha"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Chaji imepungua. Washa Kiokoa Betri"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Imebakisha <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>, itadumu takribani <xliff:g id="TIME">%s</xliff:g> kulingana na jinsi unavyotumia"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>, itadumu takribani <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>. Umewasha Kiokoa Betri."</string> <string name="invalid_charger" msgid="4549105996740522523">"Chaji ya USB haihamiliwi.\n Tumia chaka iliyopeanwa."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Kuchaji kwa kutumia USB hakutumiki."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Mtumiaji aliyeingia katika akaunti kwa kutumia kifaa hiki kwa sasa hawezi kuwasha utatuzi wa USB. Ili utumie kipengele hiki, tumia akaunti ya mtumiaji wa msingi."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Kuza ili kujaza skrini"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Tanua ili kujaza skrini"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Picha ya skrini"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Inahifadhi picha ya skrini..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Inahifadhi picha ya skrini..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Picha ya skrini inahifadhiwa"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Gusa ili utazame picha ya skrini uliyohifadhi."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Inahifadhi picha ya skrini"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Imehifadhi picha ya skrini"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Gusa ili utazame picha ya skrini uliyohifadhi"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Imeshindwa kupiga picha ya skrini"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Hitilafu imetokea wakati wa kuhifadhi picha ya skrini"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Machaguo ya uhamisho wa faili la USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Angika kama kichezaji cha maudhui (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Imezimwa"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Ukiwa na udhibiti wa arifa, unaweza kuweka kiwango cha umuhimu wa arifa za programu kuanzia 0 hadi 5. \n\n"<b>"Kiwango cha 5"</b>" \n- Onyesha katika sehemu ya juu ya orodha ya arifa \n- Ruhusu ukatizaji wa skrini nzima \n- Ruhusu arifa za kuchungulia kila wakati\n\n"<b>"Kiwango cha 4"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Ruhusu arifa za kuchungulia kila wakati \n\n"<b>"Kiwango cha 3"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia\n\n"<b>"Kiwango cha 2"</b>" \n- Zuia ukatizaji wa skrini nzima\n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti au mtetemo \n\n"<b>"Kiwango cha 1"</b>" \n- Zuia ukatizaji wa skrini nzima \n- Usiruhusu kamwe arifa za kuchungulia \n- Usiruhusu kamwe sauti na mtetemo \n- Usionyeshe skrini iliyofungwa na sehemu ya arifa \n- Onyesha katika sehemu ya chini ya orodha ya arifa \n\n"<b>"Kiwango cha 0"</b>" \n- Zuia arifa zote kutoka programu"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Arifa"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Hutapokea arifa hizi tena"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Aina <xliff:g id="NUMBER">%d</xliff:g> za arifa"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Programu hii haina aina za arifa"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Arifa kutoka kwenye programu hii haziwezi kuzimwa"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Aina 1 ya arifa kati ya <xliff:g id="NUMBER_1">%s</xliff:g> kutoka kwenye kifaa hiki</item> - <item quantity="one">Aina 1 ya arifa kati ya <xliff:g id="NUMBER_0">%s</xliff:g> kutoka kwenye kifaa hiki</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> na vingine <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> na kingine <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Hutaona tena arifa hizi"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Ungependa kuendelea kuonyesha arifa hizi?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Acha kuonyesha arifa"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Endelea kuonyesha"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Ungependa kuendelea kuonyesha arifa kutoka programu hii?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Huwezi kuzima arifa hizi"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Vidhibiti vya arifa <xliff:g id="APP_NAME">%1$s</xliff:g> vimefunguliwa"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Vidhibiti vya arifa vya <xliff:g id="APP_NAME">%1$s</xliff:g> vimefungwa"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Ruhusu arifa kutoka kwenye kituo hiki"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Aina Zote"</string> <string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Badilisha upendavyo: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Weka mapendeleo"</string> <string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string> + <string name="inline_undo" msgid="558916737624706010">"Tendua"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"vidhibiti vya arifa"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"chaguo za kuahirisha arifa"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 544cd7989ea9..5a209b0d1b14 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"செயலில் இருக்கும்"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"அறிவிப்புகள்"</string> <string name="battery_low_title" msgid="6456385927409742437">"பேட்டரி குறைவு"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> உள்ளது"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> மீதமுள்ளது. பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டுள்ளது."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB மூலம் சார்ஜ் செய்வது ஆதரிக்கப்படவில்லை.\nவழங்கப்பட்ட சார்ஜரை மட்டும் பயன்படுத்தவும்."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB சார்ஜிங் ஆதரிக்கப்படவில்லை."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"தற்போது இந்தச் சாதனத்தில் உள்நுழைந்துள்ள பயனரால் USB பிழைத்திருத்தத்தை இயக்க முடியாது. இந்த அம்சத்தை இயக்க, முதன்மைப் பயனருக்கு மாறவும்."</string> <string name="compat_mode_on" msgid="6623839244840638213">"திரையை நிரப்ப அளவை மாற்று"</string> <string name="compat_mode_off" msgid="4434467572461327898">"திரையை நிரப்ப இழு"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"ஸ்க்ரீன் ஷாட் சேமிக்கப்படுகிறது."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"ஸ்கிரீன் ஷாட் எடுக்கப்பட்டது."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"ஸ்கிரீன்ஷாட்டைப் பார்க்க, தட்டவும்."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ஸ்க்ரீன் ஷாட்டை எடுக்க முடியவில்லை."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ஸ்க்ரீன்ஷாட்டைச் சேமிக்கும் போது, பிழை ஏற்பட்டது."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, பயன்பாடு அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB கோப்பு இடமாற்ற விருப்பங்கள்"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"(MTP) மீடியா பிளேயராக ஏற்று"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ஆஃப்"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ஆற்றல்மிக்க அறிவிப்புக் கட்டுப்பாடுகள் மூலம், பயன்பாட்டின் அறிவிப்புகளுக்கு முக்கியத்துவ நிலையை (0-5) அமைக்கலாம். \n\n"<b>"நிலை 5"</b>" \n- அறிவிப்புப் பட்டியலின் மேலே காட்டும் \n- முழுத் திரைக் குறுக்கீட்டை அனுமதிக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 4"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- எப்போதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டும் \n\n"<b>"நிலை 3"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n\n"<b>"நிலை 2"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது, அதிர்வுறாது \n\n"<b>"நிலை 1"</b>" \n- முழுத் திரைக் குறுக்கீட்டைத் தடுக்கும் \n- ஒருபோதும் நடப்புத் திரையின் மேல் பகுதியில் அறிவிப்புகளைக் காட்டாது \n- ஒருபோதும் ஒலி எழுப்பாது அல்லது அதிர்வுறாது \n- பூட்டுத்திரை மற்றும் நிலைப்பட்டியிலிருந்து மறைக்கும் \n- அறிவிப்புகள் பட்டியலின் கீழே காட்டும் \n\n"<b>"நிலை 0"</b>" \n- பயன்பாட்டின் எல்லா அறிவிப்புகளையும் தடுக்கும்"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"அறிவிப்புகள்"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"இந்த அறிவிப்புகளை இனி பெறமாட்டீர்கள்"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> அறிவிப்பு வகைகள்"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"இந்தப் பயன்பாட்டில் அறிவிப்பு வகைகள் இல்லை"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"இந்தப் பயன்பாட்டிலிருந்து அறிவிப்புகளைப் பெறுவதை முடக்க முடியாது"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">இந்தப் பயன்பாடு வழங்கும் <xliff:g id="NUMBER_1">%s</xliff:g> அறிவிப்பு வகைகளில் ஒரு அறிவிப்பு வகை</item> - <item quantity="one">இந்தப் பயன்பாடு வழங்கும் <xliff:g id="NUMBER_0">%s</xliff:g> அறிவிப்பு வகையில் ஒரு அறிவிப்பு வகை</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, மேலும் <xliff:g id="NUMBER_5">%3$d</xliff:g> சேனல்கள்</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>, மேலும் <xliff:g id="NUMBER_2">%3$d</xliff:g> சேனல்</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் திறக்கப்பட்டன"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் மூடப்பட்டன"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"இந்தச் சேனலிலிருந்து அறிவிப்புகளைப் பெறுவதை அனுமதிக்கும்"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"எல்லா வகைகளும்"</string> <string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"தனிப்பயனாக்கு: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"அறிவிப்புக் கட்டுப்பாடுகள்"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"அறிவிப்பை உறக்கநிலையாக்கும் விருப்பங்கள்"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"விரி"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"சிறிதாக்கு"</string> <string name="pip_phone_close" msgid="8416647892889710330">"மூடு"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"அமைப்புகள்"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"நிராகரிக்க, கீழே இழுக்கவும்"</string> <string name="pip_menu_title" msgid="4707292089961887657">"மெனு"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> தற்போது பிக்ச்சர்-இன்-பிக்ச்சரில் உள்ளது"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index c2e0dcee245d..f8a14d04221b 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"కొనసాగుతున్నవి"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"నోటిఫికేషన్లు"</string> <string name="battery_low_title" msgid="6456385927409742437">"బ్యాటరీ తక్కువగా ఉంది"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది. బ్యాటరీ సేవర్ ఆన్లో ఉంది."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB ఛార్జింగ్కు మద్దతు లేదు.\nఅందించిన ఛార్జర్ను మాత్రమే ఉపయోగించండి."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB ఛార్జింగ్కి మద్దతు లేదు."</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ఈ పరికరానికి ప్రస్తుతం సైన్ ఇన్ చేసిన వినియోగదారు USB డీబగ్గింగ్ ఆన్ చేయలేరు. ఈ ఫీచర్ ఉపయోగించడానికి, ప్రాథమిక వినియోగదారుకి మారాలి."</string> <string name="compat_mode_on" msgid="6623839244840638213">"స్క్రీన్కు నింపేలా జూమ్ చేయండి"</string> <string name="compat_mode_off" msgid="4434467572461327898">"స్క్రీన్కు నింపేలా విస్తరించండి"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"స్క్రీన్షాట్ సేవ్ చేయబడుతోంది."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"స్క్రీన్షాట్ క్యాప్చర్ చేయబడింది."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"మీ స్క్రీన్షాట్ను వీక్షించడానికి నొక్కండి."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"స్క్రీన్షాట్ను క్యాప్చర్ చేయడం సాధ్యపడలేదు."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"స్క్రీన్షాట్ని సేవ్ చేస్తున్నప్పుడు సమస్య సంభవించింది."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"పరిమిత నిల్వ స్థలం కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు."</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"స్క్రీన్షాట్లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB ఫైల్ బదిలీ ఎంపికలు"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"మీడియా ప్లేయర్గా (MTP) మౌంట్ చేయి"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ఆఫ్లో ఉన్నాయి"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్లు బ్లాక్ చేయబడతాయి"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"నోటిఫికేషన్లు"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"మీరు ఇకపై ఈ నోటిఫికేషన్లను పొందరు"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> నోటిఫికేషన్ వర్గాలు"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"ఈ అనువర్తనానికి నోటిఫికేషన్ వర్గాలు లేవు"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ఈ యాప్ నుండి వచ్చే నోటిఫికేషన్లను ఆఫ్ చేయలేరు"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">ఈ యాప్ నుంచి <xliff:g id="NUMBER_1">%s</xliff:g> నోటిఫికేషన్ వర్గాలలో 1</item> - <item quantity="one">ఈ యాప్ నుంచి <xliff:g id="NUMBER_0">%s</xliff:g> నోటిఫికేషన్ వర్గంలో 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> మరియు మరో <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> మరియు మరో <xliff:g id="NUMBER_2">%3$d</xliff:g></item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు తెరవబడ్డాయి"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు మూసివేయబడ్డాయి"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ఈ ఛానెల్ యొక్క నోటిఫికేషన్లను అనుమతించండి"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"అన్ని వర్గాలు"</string> <string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్లు"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"అనుకూలీకరించండి: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"నోటిఫికేషన్ నియంత్రణలు"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"నోటిఫికేషన్ తాత్కాలిక ఆపివేత ఎంపికలు"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"విస్తరింపజేయి"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"కనిష్టీకరించు"</string> <string name="pip_phone_close" msgid="8416647892889710330">"మూసివేయి"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"సెట్టింగ్లు"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"తీసివేయడానికి కిందికి లాగండి"</string> <string name="pip_menu_title" msgid="4707292089961887657">"మెను"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> చిత్రంలో చిత్రం రూపంలో ఉంది"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 5b3106135c38..01b75e33f985 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ดำเนินอยู่"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"การแจ้งเตือน"</string> <string name="battery_low_title" msgid="6456385927409742437">"แบตเตอรี่เหลือน้อย"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"แบตเตอรี่เหลือน้อย โปรดเปิดโหมดประหยัดแบตเตอรี่"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"เหลืออีก <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"เหลือ <xliff:g id="PERCENTAGE">%s</xliff:g> หรืออีกราว <xliff:g id="TIME">%s</xliff:g> ขึ้นอยู่กับการใช้งานของคุณ"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"เหลือ <xliff:g id="PERCENTAGE">%s</xliff:g> หรืออีกราว <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"แบตเตอรี่เหลือ <xliff:g id="PERCENTAGE">%s</xliff:g> เปิดโหมดประหยัดแบตเตอรี่อยู่"</string> <string name="invalid_charger" msgid="4549105996740522523">"ไม่สนับสนุนการชาร์จแบบ USB\nใช้เฉพาะที่ชาร์จที่ให้มาเท่านั้น"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"ไม่รองรับการชาร์จผ่าน USB"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ผู้ใช้ที่ลงชื่อเข้าใช้อุปกรณ์อยู่ในขณะนี้ไม่สามารถเปิดการแก้ไขข้อบกพร่องผ่าน USB ได้ หากต้องการใช้ฟีเจอร์นี้ ให้เปลี่ยนไปเป็นผู้ใช้หลัก"</string> <string name="compat_mode_on" msgid="6623839244840638213">"ขยายจนเต็มหน้าจอ"</string> <string name="compat_mode_off" msgid="4434467572461327898">"ยืดจนเต็มหน้าจอ"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"ภาพหน้าจอ"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"กำลังบันทึกภาพหน้าจอ..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"กำลังบันทึกภาพหน้าจอ..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"กำลังบันทึกภาพหน้าจอ"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"จับภาพหน้าจอแล้ว"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"แตะเพื่อดูภาพหน้าจอของคุณ"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"ไม่สามารถจับภาพหน้าจอ"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"พบปัญหาขณะกำลังบันทึกภาพหน้าจอ"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ไม่สามารถบันทึกภาพหน้าจอเนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"กำลังบันทึกภาพหน้าจอ"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"บันทึกภาพหน้าจอแล้ว"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"แตะเพื่อดูภาพหน้าจอ"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"จับภาพหน้าจอไม่ได้"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"พบปัญหาขณะบันทึกภาพหน้าจอ"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string> <string name="usb_preference_title" msgid="6551050377388882787">"ตัวเลือกการถ่ายโอนไฟล์ USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ต่อเชื่อมเป็นโปรแกรมเล่นสื่อ (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ปิด"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"ส่วนควบคุมการแจ้งเตือนแบบเปิด/ปิดช่วยให้คุณตั้งค่าระดับความสำคัญสำหรับการแจ้งเตือนของแอปได้ตั้งแต่ระดับ 0-5 \n\n"<b>"ระดับ 5"</b>" \n- แสดงที่ด้านบนของรายการแจ้งเตือน \n- อนุญาตให้รบกวนแบบเต็มหน้าจอ \n- อนุญาตให้แสดงชั่วครู่ \n\n"<b>"ระดับ 4"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- แสดงชั่วครู่เสมอ \n\n"<b>"ระดับ 3"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n\n"<b>"ระดับ 2"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n- ไม่ส่งเสียงหรือสั่นเลย \n\n"<b>"ระดับ 1"</b>" \n- ป้องกันการรบกวนแบบเต็มหน้าจอ \n- ไม่แสดงชั่วครู่เลย \n- ไม่ส่งเสียงหรือสั่นเลย \n- ซ่อนจากหน้าจอล็อกและแถบสถานะ \n- แสดงที่ด้านล่างของรายการแจ้งเตือน \n\n"<b>"ระดับ 0"</b>" \n- บล็อกการแจ้งเตือนทั้งหมดจากแอป"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"การแจ้งเตือน"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"คุณจะไม่ได้รับการแจ้งเตือนเหล่านี้อีก"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"หมวดหมู่การแจ้งเตือน <xliff:g id="NUMBER">%d</xliff:g> หมวด"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"แอปนี้ไม่มีหมวดหมู่การแจ้งเตือน"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"ไม่สามารถปิดการแจ้งเตือนจากแอปนี้"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">การแจ้งเตือน 1 ใน <xliff:g id="NUMBER_1">%s</xliff:g> หมวดหมู่จากแอปนี้</item> - <item quantity="one">การแจ้งเตือน 1 ใน <xliff:g id="NUMBER_0">%s</xliff:g> หมวดหมู่จากแอปนี้</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> และอีก <xliff:g id="NUMBER_5">%3$d</xliff:g> ช่องทาง</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> และอีก <xliff:g id="NUMBER_2">%3$d</xliff:g> ช่องทาง</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"คุณจะไม่เห็นการแจ้งเตือนเหล่านี้อีก"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"แสดงการแจ้งเตือนเหล่านี้ต่อไปไหม"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"ปิดการแจ้งเตือน"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"แสดงต่อไป"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ปิดการแจ้งเตือนเหล่านี้ไม่ได้"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> เปิดอยู่"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> ปิดอยู่"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"อนุญาตการแจ้งเตือนจากช่องนี้"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"ทุกหมวดหมู่"</string> <string name="notification_more_settings" msgid="816306283396553571">"การตั้งค่าเพิ่มเติม"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"ปรับแต่ง: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"ปรับแต่ง"</string> <string name="notification_done" msgid="5279426047273930175">"เสร็จสิ้น"</string> + <string name="inline_undo" msgid="558916737624706010">"เลิกทำ"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"ส่วนควบคุมการแจ้งเตือน"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ตัวเลือกการปิดเสียงแจ้งเตือนชั่วคราว"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"ขยาย"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"ย่อเล็กสุด"</string> <string name="pip_phone_close" msgid="8416647892889710330">"ปิด"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"การตั้งค่า"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ลากลงเพื่อปิด"</string> <string name="pip_menu_title" msgid="4707292089961887657">"เมนู"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index dadd57eb87e7..5352cf0497ba 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Nagpapatuloy"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Mga Notification"</string> <string name="battery_low_title" msgid="6456385927409742437">"Mahina na ang baterya"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Paubos na ang baterya. I-on ang Pangtipid sa Baterya"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> na lang ang natitira"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> na lang ang natitira, humigit-kumulang <xliff:g id="TIME">%s</xliff:g> ang natitira batay sa iyong paggamit"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> na lang ang natitira, humigit-kumulang <xliff:g id="TIME">%s</xliff:g> ang natitira"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> na lang ang natitira. Naka-on ang Pangtipid sa Baterya."</string> <string name="invalid_charger" msgid="4549105996740522523">"Hindi sinusuportahan ang pag-charge sa USB.\nGamitin lang ang ibinigay na charger."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Hindi sinusuportahan ang pagtsa-charge gamit ang USB."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Hindi mao-on ng user na kasalukuyang naka-sign in sa device na ito ang pag-debug ng USB. Upang magamit ang feature na ito, lumipat sa pangunahing user."</string> <string name="compat_mode_on" msgid="6623839244840638213">"I-zoom upang punan screen"</string> <string name="compat_mode_off" msgid="4434467572461327898">"I-stretch upang mapuno screen"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Sine-save ang screenshot…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Sine-save ang screenshot…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Sine-save ang screenshot."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Nakuha ang screenshot."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"I-tap upang tingnan ang iyong screenshot."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Hindi makuha ang screenshot."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Nagkaroon ng problema habang sine-save ang screenshot."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Sine-save ang screenshot"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Na-save ang screenshot"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"I-tap upang tingnan ang iyong screenshot"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Hindi makunan ng screenshot"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Nagkaproblema habang sine-save ang screenshot"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Opsyon paglipat ng USB file"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"I-mount bilang isang media player (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Naka-off"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Sa pamamagitan ng mga kontrol sa notification ng power, magagawa mong itakda ang antas ng kahalagahan ng mga notification ng isang app mula 0 hanggang 5. \n\n"<b>"Antas 5"</b>" \n- Ipakita sa itaas ng listahan ng notification \n- Payagan ang pag-istorbo kapag full screen \n- Palaging sumilip \n\n"<b>"Antas 4"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Palaging sumilip \n\n"<b>"Antas 3"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n\n"<b>"Antas 2"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n- Huwag kailanman tumunog o mag-vibrate \n\n"<b>"Antas 1"</b>" \n- Pigilan ang pag-istorbo kapag full screen \n- Huwag kailanman sumilip \n- Huwag kailanman tumunog o mag-vibrate \n- Itago sa lock screen at status bar \n- Ipakita sa ibaba ng listahan ng notification \n\n"<b>"Antas 0"</b>" \n- I-block ang lahat ng notification mula sa app"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Mga Notification"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Hindi ka na makakatanggap ng ganitong mga notification"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> (na) kategorya ng notification"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Walang kategorya ng notification ang app na ito"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Hindi maaaring i-off ang mga notification mula sa app na ito"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 sa <xliff:g id="NUMBER_1">%s</xliff:g> kategorya ng notification mula sa app na ito</item> - <item quantity="other">1 sa <xliff:g id="NUMBER_1">%s</xliff:g> na kategorya ng notification mula sa app na ito</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, at <xliff:g id="NUMBER_5">%3$d</xliff:g> pang iba</item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, at <xliff:g id="NUMBER_5">%3$d</xliff:g> pang iba</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Hindi mo na makikita ang mga notification na ito"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Patuloy na ipakita ang mga notification na ito?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Ihinto ang mga notification"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Patuloy na ipakita"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Patuloy na ipakita ang mga notification mula sa app na ito?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Hindi maaaring i-off ang mga notification na ito"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Binuksan ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Isinara ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Payagan ang mga notification mula sa channel na ito"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Lahat ng Kategorya"</string> <string name="notification_more_settings" msgid="816306283396553571">"Higit pang mga setting"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"I-customize: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"I-customize"</string> <string name="notification_done" msgid="5279426047273930175">"Tapos Na"</string> + <string name="inline_undo" msgid="558916737624706010">"I-undo"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"mga kontrol ng notification"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"mga opsyon sa pag-snooze ng notification"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Palawakin"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"I-minimize"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Isara"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Mga Setting"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"I-drag pababa upang i-dismiss"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index d2d8ac659e38..092cfa50606d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string> <string name="battery_low_title" msgid="6456385927409742437">"Pil gücü düşük"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Pil az. Pil Tasarrufu\'nu açın"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> kaldı"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> kaldı (kullanımınıza göre yaklaşık <xliff:g id="TIME">%s</xliff:g>)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> (yaklaşık <xliff:g id="TIME">%s</xliff:g>) kaldı"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> kaldı. Pil Tasarrufu açık."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB üzerinden şarj desteklenmiyor.\nYalnızca ürünle birlikte verilen şarj cihazını kullanın."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB şarjı desteklenmiyor."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Bu cihazda geçerli olarak oturum açmış olan kullanıcı, USB hata ayıklama özelliğini açamaz. Bu özelliği kullanmak için birincil kullanıcıya geçin."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Yakınlaştır (ekranı kaplasın)"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Genişlet (ekran kapansın)"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ekran görüntüsü"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Ekran görüntüsü kaydediliyor..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Ekran görüntüsü kaydediliyor..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Ekran görüntüsü kaydediliyor."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran görüntüsü alındı."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Ekran görüntünüzü görüntülemek için dokunun."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Ekran görüntüsü alınamadı."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Ekran görüntüsü kaydedilirken sorun oluştu."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Ekran görüntüsü kaydediliyor"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Ekran görüntüsü kaydedildi"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Ekran görüntünüzü görmek için dokunun"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Ekran görüntüsü alınamadı"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Ekran görüntüsü kaydedilirken sorun oluştu"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB dosya aktarım seçenekleri"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Medya oynatıcı olarak ekle (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Kapalı"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Güç bildirim kontrolleriyle, bir uygulamanın bildirimleri için 0 ile 5 arasında bir önem düzeyi ayarlayabilirsiniz. \n\n"<b>"5. Düzey"</b>" \n- Bildirim listesinin en üstünde gösterilsin \n- Tam ekran kesintisine izin verilsin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"4. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda her zaman kısaca belirsin \n\n"<b>"3. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n\n"<b>"2. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman belirmesin \n- Hiçbir zaman ses çıkarmasın ve titreştirmesin \n\n"<b>"1. Düzey"</b>" \n- Tam ekran kesintisi engellensin \n- Ekranda hiçbir zaman kısaca belirmesin \n- Hiçbir zaman ses çıkarmasın veya titreştirmesin \n- Kilit ekranından ve durum çubuğundan gizlensin \n- Bildirim listesinin en altında gösterilsin \n\n"<b>"0. Düzey"</b>" \n- Uygulamadan gelen tüm bildirimler engellensin"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirimler"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Bu bildirimleri artık almayacaksınız"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> bildirim kategorisi"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Bu uygulamanın bildirim kategorisi yok"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Bu uygulamanın bildirimleri kapatılamaz"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Bu uygulamadaki <xliff:g id="NUMBER_1">%s</xliff:g> bildirim kategorisinden 1 tanesi</item> - <item quantity="one">Bu uygulamadaki <xliff:g id="NUMBER_0">%s</xliff:g> bildirim kategorisinden 1 tanesi</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> ve diğer <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> ve <xliff:g id="NUMBER_2">%3$d</xliff:g> tane daha</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Bu bildirimleri artık görmeyeceksiniz"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Bu bildirimler gösterilmeye devam edilsin mi?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Bildirimleri durdur"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Göstermeye devam et"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu uygulamadan gelen bildirimler gösterilmeye devam edilsin mi?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Bu bildirimler kapatılamaz"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri açıldı"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri kapatıldı"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Bu kanaldan bildirimlere izin verir"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Tüm Kategoriler"</string> <string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Özelleştir: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Özelleştir"</string> <string name="notification_done" msgid="5279426047273930175">"Bitti"</string> + <string name="inline_undo" msgid="558916737624706010">"Geri al"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"Bildirim kontrolleri"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"bildirim erteleme seçenekleri"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Genişlet"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Simge durumuna getir"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Kapat"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Ayarlar"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Kapatmak için aşağıya sürükleyin"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menü"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 23db95b58e77..07908479325c 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -35,7 +35,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string> <string name="battery_low_title" msgid="6456385927409742437">"Низький рівень заряду акумулятора"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Акумулятор майже розрядився. Увімкніть режим економії заряду акумулятора"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"На основі використання залишилося <xliff:g id="PERCENTAGE">%s</xliff:g> – близько <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g> – близько <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Залишилося <xliff:g id="PERCENTAGE">%s</xliff:g>. Увімкнено режим економії заряду акумулятора."</string> <string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується.\nВикористовуйте лише наданий у комплекті зарядний пристрій."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Заряджання через USB не підтримується."</string> @@ -69,14 +72,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Користувач поточного облікового запису не може вмикати налагодження USB. Щоб увімкнути цю функцію, увійдіть в обліковий запис основного користувача."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Масштабув. на весь екран"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Розтягнути на весь екран"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Знімок екрана"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Збереження знімка екрана..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Збереження знімка екрана..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Зберігається знімок екрана."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Знімок екрана зроблено."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Торкніться, щоб переглянути знімок екрана."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Не вдалося зробити знімок екрана."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Не вдалося зберегти знімок екрана."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Знімок екрана зберігається"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Знімок екрана збережено"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Торкніться, щоб переглянути знімок екрана"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Не вдалося зробити знімок екрана"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Не вдалося зберегти знімок екрана"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Парам.передав.файлів через USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Підключити як медіапрогравач (MTP)"</string> @@ -563,30 +567,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Вимк."</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"За допомогою елементів керування сповіщеннями ви можете налаштувати пріоритет сповіщень додатка – від 0 до 5 рівня. \n\n"<b>"Рівень 5"</b>\n"- Показувати сповіщення вгорі списку \n- Виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 4"</b>\n"- Не виводити на весь екран \n- Завжди показувати короткі сповіщення \n\n"<b>"Рівень 3"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n\n"<b>"Рівень 2"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n\n"<b>"Рівень 1"</b>\n"- Не виводити на весь екран \n- Не показувати короткі сповіщення \n- Вимкнути звук і вібросигнал \n- Не показувати на заблокованому екрані та в рядку стану \n- Показувати сповіщення внизу списку \n\n"<b>"Рівень 0"</b>\n"- Блокувати всі сповіщення з додатка"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Сповіщення"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ви більше не отримуватимете ці сповіщення"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"Категорії сповіщень (<xliff:g id="NUMBER">%d</xliff:g>)"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"У цьому додатку немає категорій сповіщень"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Сповіщення з цього додатка не можна вимкнути"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорії сповіщень із цього додатка</item> - <item quantity="few">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорій сповіщень із цього додатка</item> - <item quantity="many">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорій сповіщень із цього додатка</item> - <item quantity="other">1 з <xliff:g id="NUMBER_1">%s</xliff:g> категорії сповіщень із цього додатка</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="few"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="many"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> і ще <xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ви більше не бачитимете цих сповіщень"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Чи показувати ці сповіщення надалі?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Не показувати сповіщення"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Показувати надалі"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Чи показувати сповіщення з цього додатка надалі?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ці сповіщення не можна вимкнути"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> відкрито"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> закрито"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Дозволити сповіщення з цього каналу"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Усі категорії"</string> <string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Налаштувати: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Налаштувати"</string> <string name="notification_done" msgid="5279426047273930175">"Готово"</string> + <string name="inline_undo" msgid="558916737624706010">"Відмінити"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"елементи керування сповіщеннями"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"параметри відкладення сповіщень"</string> @@ -745,8 +738,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Розгорнути"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Згорнути"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Закрити"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Налаштування"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Перетягніть униз, щоб закрити"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Меню"</string> <string name="pip_notification_title" msgid="3204024940158161322">"У додатку <xliff:g id="NAME">%s</xliff:g> є функція \"Картинка в картинці\""</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index ad5ac98e310d..ffcb60a619f4 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"جاری"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"اطلاعات"</string> <string name="battery_low_title" msgid="6456385927409742437">"بیٹری کم ہے"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"بیٹری کم ہے۔ بیٹری سیور آن کریں"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی، آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%s</xliff:g> باقی ہے"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی، تقریباً <xliff:g id="TIME">%s</xliff:g> باقی ہے"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے۔ بیٹری سیور آن ہے۔"</string> <string name="invalid_charger" msgid="4549105996740522523">"USB چارجنگ تعاون یافتہ نہیں ہے.\nصرف فراہم کردہ چارجر کا ہی استعمال کریں۔"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB چارجنگ تعاون یافتہ نہیں ہے۔"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"اس آلہ پر فی الحال سائن ان کردہ صارف USB ڈیبگنگ آن نہیں کر سکتا۔ اس خصوصیت کا استعمال کرنے کیلئے، ابتدائی صارف پر سوئچ کریں۔"</string> <string name="compat_mode_on" msgid="6623839244840638213">"پوری سکرین پر زوم کریں"</string> <string name="compat_mode_off" msgid="4434467572461327898">"پوری سکرین پر پھیلائیں"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"اسکرین شاٹ"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"اسکرین شاٹ محفوظ کیا جا رہا ہے۔"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"اسکرین شاٹ کیپچر کیا گیا۔"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"اپنا اسکرین شاٹ دیکھنے کیلئے تھپتھپائیں۔"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"اسکرین شاٹ کیپچر نہیں کر سکے۔"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"اسکرین شاٹ محفوظ کرتے وقت مسئلہ پیش آ گیا۔"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"محدود اسٹوریج جگہ کی وجہ سے اسکرین شاٹس نہیں لئے جا سکتے۔"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"اسکرین شاٹ محفوظ کیا جا رہا ہے"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"اسکرین شاٹ محفوظ ہو گیا"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"اپنا اسکرین شاٹ دیکھنے کیلئے تھپتھپائیں"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"اسکرین شاٹ کیپچر نہیں ہو سکا"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"اسکرین شاٹ محفوظ کرتے وقت مسئلہ پیش آ گیا"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB فائل منتقل کرنیکے اختیارات"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"ایک میڈیا پلیئر (MTP) کے بطور ماؤنٹ کریں"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"آف"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"پاور اطلاع کنٹرولز کے ساتھ آپ کسی ایپ کی اطلاعات کیلئے 0 سے 5 تک اہمیت کی سطح سیٹ کر سکتے ہیں۔ \n\n"<b>"سطح 5"</b>\n"- اطلاعات کی فہرست کے اوپر دکھائیں \n- پوری اسکرین کی مداخلت کی اجازت دیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 4"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- ہمیشہ جھانکنا\n\n"<b>"سطح 3"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n\n"<b>"سطح 2"</b>\n"- پوری اسکرین کی مداخلت کو روکیں \n- کبھی نہ جھانکنا \n- کبھی آواز اور ارتعاش پیدا نہ کرنا \n\n"<b>" سطح 1"</b>\n"- پوری اسکرین کی مداخلت کو روکنا \n- کبھی نہ جھانکنا \n- کبھی بھی آواز یا ارتعاش پیدا نہ کرنا\n- مقفل اسکرین اور اسٹیٹس بار سے چھپانا \n - اطلاع کی فہرست کی نیچے دکھانا \n\n"<b>"سطح 0"</b>\n"- ایپ سے تمام اطلاعات مسدود کریں"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"اطلاعات"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"آپ کو یہ اطلاعات مزید نہیں ملیں گی"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"اطلاع کے <xliff:g id="NUMBER">%d</xliff:g> زمرے"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"اس ایپ میں اطلاعاتی زمرے نہیں ہیں"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"اس ایپ کی اطلاعات کو آف نہیں کیا جا سکتا"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">اس ایپ کے <xliff:g id="NUMBER_1">%s</xliff:g> اطلاعاتی زمروں میں سے 1</item> - <item quantity="one">اس ایپ کے <xliff:g id="NUMBER_0">%s</xliff:g> اطلاعاتی زمرے میں سے 1</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> اور <xliff:g id="NUMBER_5">%3$d</xliff:g> دیگر</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>، <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> اور <xliff:g id="NUMBER_2">%3$d</xliff:g> دیگر</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"آپ کو یہ اطلاعات مزید دکھائی نہیں دیں گی"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"یہ اطلاعات دکھانا جاری رکھیں؟"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"اطلاعات روک دیں"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"دکھانا جاری رکھیں"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"اس ایپ کی طرف سے اطلاعات دکھانا جاری رکھیں؟"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"ان اطلاعات کو آف نہیں کیا جا سکتا"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز کھلے ہیں"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز بند کر دئے گئے ہیں"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"اس چینل سے اطلاعات کی اجازت دیں"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"سبھی زمرے"</string> <string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"حسب ضرورت بنائیں: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"حسب ضرورت بنائیں"</string> <string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string> + <string name="inline_undo" msgid="558916737624706010">"کالعدم کریں"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"اطلاع کے کنٹرولز"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"اطلاع اسنوز کرنے کے اختیارات"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"پھیلائیں"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"چھوٹی کریں"</string> <string name="pip_phone_close" msgid="8416647892889710330">"بند کریں"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"ترتیبات"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"برخاست کرنے کیلئے نیچے گھسیٹیں"</string> <string name="pip_menu_title" msgid="4707292089961887657">"مینو"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> تصویر میں تصویر میں ہے"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 0db8ef245b9e..a8b5e54df3d5 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Hali bajarilmagan"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Eslatmalar"</string> <string name="battery_low_title" msgid="6456385927409742437">"Batareya quvvati kam qoldi"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batareya quvvati kam qoldi. Quvvat tejash rejimini yoqing."</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> qoldi"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> (joriy holatda taxminan <xliff:g id="TIME">%s</xliff:g> qoldi)"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> (taxminan <xliff:g id="TIME">%s</xliff:g> qoldi)"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> qoldi. Quvvat tejash rejimi yoniq."</string> <string name="invalid_charger" msgid="4549105996740522523">"USB orqali zaryadlab bo‘lmaydi.\nFaqat taklif qilingan zaryadlagichdan foydalaning."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"USB orqali quvvat oldirish qo‘llab-quvvatlanmaydi."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ayni paytda ushbu qurilmaga o‘z hisobi bilan kirgan foydalanuvchi USB orqali nosozliklarni tuzatish funksiyasini yoqa olmaydi. Bu funksiyadan foydalanish uchun asosiy foydalanuvchi profiliga o‘ting."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Ekranga moslashtirish"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Ekran hajmida cho‘zish"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Skrinshot"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Skrinshot saqlanmoqda…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Skrinshot saqlanmoqda…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Skrinshot saqlanmoqda."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Skrinshot saqlandi."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Skrinshotni ko‘rish uchun bosing."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Skrinshot saqlanmadi."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Skrinshotni saqlashda muammo yuz berdi."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Xotirada joy kamligi uchun skrinshotni saqlab bo‘lmadi."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Skrinshot saqlanmoqda"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Skrinshot saqlandi"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Skrinshotni ko‘rish uchun bosing"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Skrinshot saqlanmadi"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Skrinshotni saqlashda muammo yuz berdi"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB fayl ko‘chirish moslamalari"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Media pleyer sifatida ulash (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"O‘chiq"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirishnomalar"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ushbu bildirishnomalar endi ko‘rsatilmaydi"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> ta bildirishnoma turkumi"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Bu ilovada bildirishnomalar turkumi yo‘q"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Bu ilova bildirishnomalarini o‘chirib bo‘lmaydi"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">Bu ilovadagi <xliff:g id="NUMBER_1">%s</xliff:g> ta bildirishnomalar turkumidan 1 tasi</item> - <item quantity="one">Bu ilovadagi <xliff:g id="NUMBER_0">%s</xliff:g> ta bildirishnomalar turkumidan 1 tasi</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> va yana <xliff:g id="NUMBER_5">%3$d</xliff:g> ta</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> va yana <xliff:g id="NUMBER_2">%3$d</xliff:g> ta</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ushbu bildirishnomalar endi ko‘rsatilmaydi"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Mazkur bildirishnomalar ko‘rsatishda davom etilsinmi?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Bildirishnomalarni to‘xtatish"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Ko‘rsatilsin"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu ilovadan keladigan bildirishnomalar ko‘rsatishda davom etilsinmi?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Ushbu bildirishnomalarni o‘chirib bo‘lmaydi"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari ochildi"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari yopildi"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Bu kanaldan keladigan bildirishnomalarga ruxsat berish"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Barcha turkumlar"</string> <string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>: sozlash"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Moslash"</string> <string name="notification_done" msgid="5279426047273930175">"Tayyor"</string> + <string name="inline_undo" msgid="558916737624706010">"Qaytarish"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"bildirishnoma sozlamalari"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"bildirishnomalarni kechiktirish parametrlari"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Yoyish"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Yig‘ish"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Yopish"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Sozlamalar"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Yopish uchun pastga torting"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menyu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 75b18caea63c..e5c25dcf9a34 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Đang diễn ra"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Thông báo"</string> <string name="battery_low_title" msgid="6456385927409742437">"Pin yếu"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Pin yếu. Bật Trình tiết kiệm pin"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"Còn lại <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Còn lại <xliff:g id="PERCENTAGE">%s</xliff:g>, còn khoảng <xliff:g id="TIME">%s</xliff:g> dựa trên mức sử dụng của bạn"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Còn lại <xliff:g id="PERCENTAGE">%s</xliff:g>, còn khoảng <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Còn lại <xliff:g id="PERCENTAGE">%s</xliff:g>. Trình tiết kiệm pin đang bật."</string> <string name="invalid_charger" msgid="4549105996740522523">"Không hỗ trợ sạc qua USB.\nChỉ sử dụng bộ sạc được cung cấp."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Sạc qua USB không được hỗ trợ."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Người dùng hiện đã đăng nhập vào thiết bị này không thể bật tính năng gỡ lỗi USB. Để sử dụng tính năng này, hãy chuyển sang người dùng chính."</string> <string name="compat_mode_on" msgid="6623839244840638213">"T.phóng để lấp đầy m.hình"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Giãn ra để lấp đầy m.hình"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Ảnh chụp màn hình"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Đang lưu ảnh chụp màn hình..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Đang lưu ảnh chụp màn hình..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Ảnh chụp màn hình đang được lưu."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Đã chụp ảnh màn hình."</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Nhấn để xem ảnh chụp màn hình của bạn."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Không thể chụp ảnh màn hình."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Đã gặp phải sự cố khi đang lưu ảnh chụp màn hình."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Đang lưu ảnh chụp màn hình"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Đã lưu ảnh chụp màn hình"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Nhấn để xem ảnh chụp màn hình của bạn"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Không thể chụp ảnh màn hình"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Đã xảy ra sự cố khi lưu ảnh chụp màn hình"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Tùy chọn truyền tệp USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Gắn như một trình phát đa phương tiện (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Tắt"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Với các kiểm soát thông báo nguồn, bạn có thể đặt cấp độ quan trọng từ 0 đến 5 cho các thông báo của ứng dụng. \n\n"<b>"Cấp 5"</b>" \n- Hiển thị ở đầu danh sách thông báo \n- Cho phép gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 4"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Luôn xem nhanh \n\n"<b>"Cấp 3"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n\n"<b>"Cấp 2"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n\n"<b>"Cấp 1"</b>" \n- Ngăn gián đoạn ở chế độ toàn màn hình \n- Không bao giờ xem nhanh \n- Không bao giờ có âm báo và rung \n- Ẩn khỏi màn hình khóa và thanh trạng thái \n- Hiển thị ở cuối danh sách thông báo \n\n"<b>"Cấp 0"</b>" \n- Chặn tất cả các thông báo từ ứng dụng"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Thông báo"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Bạn sẽ không nhận được những thông báo này nữa"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> danh mục thông báo"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Ứng dụng này không có loại thông báo"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Không thể tắt thông báo từ ứng dụng này"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">1 trên tổng số <xliff:g id="NUMBER_1">%s</xliff:g> loại thông báo từ ứng dụng này</item> - <item quantity="one">1 trên tổng số <xliff:g id="NUMBER_0">%s</xliff:g> loại thông báo từ ứng dụng này</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> và <xliff:g id="NUMBER_5">%3$d</xliff:g> kênh khác</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> và <xliff:g id="NUMBER_2">%3$d</xliff:g> kênh khác</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Bạn sẽ không thấy các thông báo này nữa"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Tiếp tục hiển thị các thông báo này?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Dừng thông báo"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Tiếp tục hiển thị"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Tiếp tục hiển thị các thông báo từ ứng dụng này?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Không thể tắt các thông báo này"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Đã mở điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Đã đóng điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Cho phép thông báo từ kênh này"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Tất cả danh mục"</string> <string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Tùy chỉnh: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Tùy chỉnh"</string> <string name="notification_done" msgid="5279426047273930175">"Xong"</string> + <string name="inline_undo" msgid="558916737624706010">"Hoàn tác"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"điều khiển thông báo"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"Tùy chọn báo lại thông báo"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"Mở rộng"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"Thu nhỏ"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Đóng"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"Cài đặt"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Kéo xuống để loại bỏ"</string> <string name="pip_menu_title" msgid="4707292089961887657">"Menu"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 16161ee16276..94a9961869f4 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -33,7 +33,13 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string> + <!-- no translation found for battery_low_title_hybrid (6268991275887381595) --> + <skip /> <string name="battery_low_percent_format" msgid="2900940511201380775">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) --> + <skip /> + <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) --> + <skip /> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"剩余 <xliff:g id="PERCENTAGE">%s</xliff:g>。省电模式已开启。"</string> <string name="invalid_charger" msgid="4549105996740522523">"不支持USB充电功能。\n只能使用随附的充电器充电。"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string> @@ -67,14 +73,22 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前已登录此设备的用户无法开启 USB 调试功能。要使用此功能,请切换为主要用户的帐号。"</string> <string name="compat_mode_on" msgid="6623839244840638213">"缩放以填满屏幕"</string> <string name="compat_mode_off" msgid="4434467572461327898">"拉伸以填满屏幕"</string> + <!-- no translation found for global_action_screenshot (8329831278085426283) --> + <skip /> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"正在保存屏幕截图..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"正在保存屏幕截图..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"正在保存屏幕截图。"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"已抓取屏幕截图。"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"点按即可查看您的屏幕截图。"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"无法抓取屏幕截图。"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"保存屏幕截图时出现问题。"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由于存储空间有限,无法保存屏幕截图。"</string> + <!-- no translation found for screenshot_saving_text (2545047868936087248) --> + <skip /> + <!-- no translation found for screenshot_saved_title (5637073968117370753) --> + <skip /> + <!-- no translation found for screenshot_saved_text (7574667448002050363) --> + <skip /> + <!-- no translation found for screenshot_failed_title (9096484883063264803) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) --> + <skip /> + <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) --> + <skip /> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"此应用或您所在的单位不允许进行屏幕截图"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB文件传输选项"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"作为媒体播放器(MTP)装载"</string> @@ -557,26 +571,27 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"关闭"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"利用高级通知设置,您可以为应用通知设置从 0 级到 5 级的重要程度等级。\n\n"<b>"5 级"</b>" \n- 在通知列表顶部显示 \n- 允许全屏打扰 \n- 一律短暂显示通知 \n\n"<b>"4 级"</b>" \n- 禁止全屏打扰 \n- 一律短暂显示通知 \n\n"<b>"3 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n\n"<b>"2 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n- 一律不发出声音或振动 \n\n"<b>"1 级"</b>" \n- 禁止全屏打扰 \n- 一律不短暂显示通知 \n- 一律不发出声音或振动 \n- 不在锁定屏幕和状态栏中显示 \n- 在通知列表底部显示 \n\n"<b>"0 级"</b>" \n- 屏蔽应用的所有通知"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"您将不会再收到这类通知"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> 个通知类别"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"此应用没有通知类别"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"无法关闭来自此应用的通知"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">此应用指定的 1 个通知类别(共 <xliff:g id="NUMBER_1">%s</xliff:g> 个)</item> - <item quantity="one">此应用指定的 1 个通知类别(共 <xliff:g id="NUMBER_0">%s</xliff:g> 个)</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>以及另外 <xliff:g id="NUMBER_5">%3$d</xliff:g> 项</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>以及另外 <xliff:g id="NUMBER_2">%3$d</xliff:g> 项</item> - </plurals> + <!-- no translation found for notification_channel_disabled (344536703863700565) --> + <skip /> + <!-- no translation found for inline_keep_showing (8945102997083836858) --> + <skip /> + <!-- no translation found for inline_stop_button (4172980096860941033) --> + <skip /> + <!-- no translation found for inline_keep_button (6665940297019018232) --> + <skip /> + <!-- no translation found for inline_keep_showing_app (1723113469580031041) --> + <skip /> + <!-- no translation found for notification_unblockable_desc (1037434112919403708) --> + <skip /> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已打开"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已关闭"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"允许接收来自此频道的通知"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"所有类别"</string> <string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"自定义:<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <!-- no translation found for notification_app_settings (420348114670768449) --> + <skip /> <string name="notification_done" msgid="5279426047273930175">"完成"</string> + <!-- no translation found for inline_undo (558916737624706010) --> + <skip /> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g><xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"通知设置"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"通知延后选项"</string> @@ -731,8 +746,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"展开"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string> <string name="pip_phone_close" msgid="8416647892889710330">"关闭"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"设置"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖动即可关闭"</string> <string name="pip_menu_title" msgid="4707292089961887657">"菜单"</string> <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g>目前位于“画中画”中"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index e0ab7e468eb1..3e624f20d2a2 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"持續進行"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="6456385927409742437">"電量低"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"電量不足。請開啟省電模式"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"電量剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>,根據您的使用情況,剩餘時間大約 <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"電量剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>,剩餘時間大約 <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>。省電模式已開啟。"</string> <string name="invalid_charger" msgid="4549105996740522523">"不支援 USB 充電。\n僅能使用隨附的充電器。"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"不支援 USB 充電功能。"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前登入此裝置的使用者無法啟用 USB 偵錯功能。如要使用此功能,請切換至主要使用者。"</string> <string name="compat_mode_on" msgid="6623839244840638213">"放大為全螢幕"</string> <string name="compat_mode_off" msgid="4434467572461327898">"放大為全螢幕"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"擷取螢幕畫面"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"正在儲存螢幕擷取畫面..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"正在儲存螢幕擷取畫面..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"正在儲存螢幕擷取畫面。"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"已擷取螢幕畫面。"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"輕按即可查看螢幕擷圖。"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"無法擷取螢幕畫面。"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"儲存螢幕擷圖時發生問題。"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由於儲存空間有限,因此無法儲存螢幕擷取畫面。"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"正在儲存螢幕擷取畫面"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"螢幕擷取畫面已儲存"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"輕按即可查看螢幕擷取畫面"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"無法擷取螢幕畫面"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"儲存螢幕擷取畫面時發生問題"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"應用程式或您的機構不允許擷取螢幕畫面"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"掛接為媒體播放器 (MTP)"</string> @@ -559,26 +563,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"關閉"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"通知控制項讓您設定應用程式通知的重要性 (0 至 5 級)。\n\n"<b>"第 5 級"</b>" \n- 在通知清單頂部顯示 \n- 允許全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 4 級"</b>" \n- 阻止全螢幕騷擾 \n- 一律顯示通知 \n\n"<b>"第 3 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n\n"<b>"第 2 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n\n"<b>"第 1 級"</b>" \n- 阻止全螢幕騷擾 \n- 永不顯示通知 \n- 永不發出聲響和震動 \n- 從上鎖畫面和狀態列中隱藏 \n- 在通知清單底部顯示 \n\n"<b>"第 0 級"</b>" \n- 封鎖所有應用程式通知"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"您不會再收到這些通知"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> 個通知類別"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"此應用程式沒有通知類別"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"無法關閉此應用程式的通知"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">此應用程式的 1 個通知類別 (共 <xliff:g id="NUMBER_1">%s</xliff:g> 個)</item> - <item quantity="one">此應用程式的 1 個通知類別 (共 <xliff:g id="NUMBER_0">%s</xliff:g> 個)</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>和另外 <xliff:g id="NUMBER_5">%3$d</xliff:g> 個頻道</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>和另外 <xliff:g id="NUMBER_2">%3$d</xliff:g> 個頻道</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"您不會再看到這些通知"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"要繼續顯示這些通知嗎?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"停止通知"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示此應用程式的通知嗎?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"開咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"閂咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"允許收到呢個頻道嘅通知"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"所有類別"</string> <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"自訂:<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"自訂"</string> <string name="notification_done" msgid="5279426047273930175">"完成"</string> + <string name="inline_undo" msgid="558916737624706010">"復原"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"通知控制項"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"通知延後選項"</string> @@ -733,8 +730,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string> <string name="pip_phone_close" msgid="8416647892889710330">"關閉"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"設定"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖曳即可關閉"</string> <string name="pip_menu_title" msgid="4707292089961887657">"選單"</string> <string name="pip_notification_title" msgid="3204024940158161322">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 2d43da875b51..e6ff9d7c4ae0 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"進行中"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string> <string name="battery_low_title" msgid="6456385927409742437">"電池電力不足"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"電池電力不足,請開啟節約耗電量模式"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"僅剩 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"電力剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>,根據你的使用情形,剩餘時間大約還有 <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"電力剩餘 <xliff:g id="PERCENTAGE">%s</xliff:g>,剩餘時間大約還有 <xliff:g id="TIME">%s</xliff:g>"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"僅剩 <xliff:g id="PERCENTAGE">%s</xliff:g>。節約耗電量模式已開啟。"</string> <string name="invalid_charger" msgid="4549105996740522523">"不支援 USB 充電。\n僅能使用隨附的充電器。"</string> <string name="invalid_charger_title" msgid="3515740382572798460">"不支援 USB 充電功能。"</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前登入這個裝置的使用者無法啟用 USB 偵錯功能。如要使用這項功能,請切換到主要使用者。"</string> <string name="compat_mode_on" msgid="6623839244840638213">"放大為全螢幕"</string> <string name="compat_mode_off" msgid="4434467572461327898">"放大為全螢幕"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"擷取螢幕畫面"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"正在儲存螢幕擷取畫面…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"正在儲存螢幕擷取畫面…"</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"正在儲存螢幕擷取畫面。"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"已拍攝螢幕擷取畫面。"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"輕觸即可查看螢幕擷圖。"</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"無法拍攝螢幕擷取畫面。"</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"儲存螢幕擷取畫面時發生問題。"</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由於儲存空間有限,因此無法儲存螢幕擷取畫面。"</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"正在儲存螢幕擷取畫面"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"螢幕擷取畫面已儲存"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"輕觸即可查看螢幕擷取畫面"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"無法擷取螢幕畫面"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"儲存螢幕擷取畫面時發生問題"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"這個應用程式或貴機構不允許擷取螢幕畫面"</string> <string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"掛接為媒體播放器 (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"關閉"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"只要使用電源通知控制項,你就能為應用程式通知設定從 0 到 5 的重要性等級。\n\n"<b>"等級 5"</b>" \n- 顯示在通知清單頂端 \n- 允許全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 4"</b>" \n- 禁止全螢幕通知 \n- 一律允許短暫顯示通知 \n\n"<b>"等級 3"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n\n"<b>"等級 2"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n\n"<b>"等級 1"</b>" \n- 禁止全螢幕通知 \n- 一律不允許短暫顯示通知 \n- 一律不發出音效或震動 \n- 在鎖定畫面和狀態列中隱藏 \n- 顯示在通知清單底端 \n\n"<b>"等級 0"</b>" \n- 封鎖應用程式的所有通知"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"通知"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"你不會再收到這類通知"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> 個通知類別"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"這個應用程式沒有通知類別"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"無法關閉這個應用程式發出的通知"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="other">在 <xliff:g id="NUMBER_1">%s</xliff:g> 個通知類別中,有 1 個類別是來自這個應用程式</item> - <item quantity="one">在 <xliff:g id="NUMBER_0">%s</xliff:g> 個通知類別中,有 1 個類別是來自這個應用程式</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>和另外 <xliff:g id="NUMBER_5">%3$d</xliff:g> 個管道</item> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>、<xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g>和另外 <xliff:g id="NUMBER_2">%3$d</xliff:g> 個管道</item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"你不會再看到這些通知"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"要繼續顯示這些通知嗎?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"停止通知"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示這個應用程式的通知嗎?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"無法關閉這些通知"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已開啟"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已關閉"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"允許來自這個頻道的通知"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"所有類別"</string> <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"自訂:<xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"自訂"</string> <string name="notification_done" msgid="5279426047273930175">"完成"</string> + <string name="inline_undo" msgid="558916737624706010">"復原"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"通知控制項"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"通知延後選項"</string> @@ -731,8 +728,7 @@ <string name="pip_phone_expand" msgid="5889780005575693909">"展開"</string> <string name="pip_phone_minimize" msgid="1079119422589131792">"最小化"</string> <string name="pip_phone_close" msgid="8416647892889710330">"關閉"</string> - <!-- no translation found for pip_phone_settings (8080777499521528521) --> - <skip /> + <string name="pip_phone_settings" msgid="8080777499521528521">"設定"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"向下拖曳即可關閉"</string> <string name="pip_menu_title" msgid="4707292089961887657">"選單"</string> <string name="pip_notification_title" msgid="3204024940158161322">"「<xliff:g id="NAME">%s</xliff:g>」目前在子母畫面中"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index e5bd3a9e1b63..a0eb56fe445e 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -33,7 +33,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Okuqhubekayo"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Izaziso"</string> <string name="battery_low_title" msgid="6456385927409742437">"Ibhethri liphansi"</string> + <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Ibhethri liphansi. Vula isilondolozi sebhethri"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele, cishe u-<xliff:g id="TIME">%s</xliff:g> osele ngokusukela ekusebenziseni kwakho"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> okusele, cishe u-<xliff:g id="TIME">%s</xliff:g> osele"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> esele. Isilondolozi sebhethri sivuliwe."</string> <string name="invalid_charger" msgid="4549105996740522523">"Ukushaja i-USB akusekelwe.\nSebenzisa kuphela ishaja enikeziwe."</string> <string name="invalid_charger_title" msgid="3515740382572798460">"Ukushaja kwe-USB akusekelwe."</string> @@ -67,14 +70,15 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Umsebenzisi manje ongene ngemvume kule divayisi entsha akakwazi ukuvula ukulungisa amaphutha ku-USB. Ukuze usebenzise lesi sici, shintshela kumsebenzisi oyinhloko."</string> <string name="compat_mode_on" msgid="6623839244840638213">"Sondeza ukugcwalisa isikrini"</string> <string name="compat_mode_off" msgid="4434467572461327898">"Nweba ukugcwalisa isikrini"</string> + <string name="global_action_screenshot" msgid="8329831278085426283">"Isithombe-skrini"</string> <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Ilondoloz umfanekiso weskrini..."</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"Ilondoloz umfanekiso weskrini..."</string> - <string name="screenshot_saving_text" msgid="2419718443411738818">"Umfanekiso weskrini uyalondolozwa."</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"Umfanekiso weskrini uqoshiwe"</string> - <string name="screenshot_saved_text" msgid="2685605830386712477">"Thepha ukuze ubuke isithombe-skrini sakho."</string> - <string name="screenshot_failed_title" msgid="705781116746922771">"Yehlulekile ukulondoloza umfanekiso weskrini."</string> - <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Inkinga ivelile ngenkathi ilondoloza isithombe sikrini."</string> - <string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe."</string> + <string name="screenshot_saving_text" msgid="2545047868936087248">"Umfanekiso weskrini uyalondolozwa"</string> + <string name="screenshot_saved_title" msgid="5637073968117370753">"Isithombe-skrini silondoloziwe"</string> + <string name="screenshot_saved_text" msgid="7574667448002050363">"Thepha ukuze ubuke isithombe-skrini sakho"</string> + <string name="screenshot_failed_title" msgid="9096484883063264803">"Yehlulekile ukuthwebula umfanekiso weskrini"</string> + <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Inkinga ivelile ngenkathi ilondoloza isithombe sikrini"</string> + <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string> <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string> <string name="usb_preference_title" msgid="6551050377388882787">"Okukhethwa kokudluliswa kwefayela ye-USB"</string> <string name="use_mtp_button_title" msgid="4333504413563023626">"Lengisa njengesidlali semediya (MTP)"</string> @@ -557,26 +561,19 @@ <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Valiwe"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Ngezilawuli zesaziso zamandla, ungasetha ileveli ebalulekile kusuka ku-0 kuya ku-5 kusuka kuzaziso zohlelo lokusebenza. \n\n"<b>"Ileveli 5"</b>" \n- Ibonisa phezulu kuhlu lwesaziso \n- Vumela ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 4"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukuhlola njalo \n\n"<b>"Ileveli 3"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n\n"<b>"Ileveli 2"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo nokudlidliza \n\n"<b>"Ileveli 1"</b>" \n- Gwema ukuphazamiseka kwesikrini esigcwele \n- Ukungahloli \n- Ungenzi umsindo noma ukudlidliza \n- Fihla kusuka kusikrini sokukhiya nebha yesimo \n- Bonisa phansi kohlu lwesaziso \n\n"<b>"Ileveli 0"</b>" \n- Vimbela zonke izaziso kusuka kuhlelo lokusebenza"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Izaziso"</string> - <string name="notification_channel_disabled" msgid="2139193533791840539">"Ngeke usathola izaziso"</string> - <string name="notification_num_channels" msgid="2048144408999179471">"<xliff:g id="NUMBER">%d</xliff:g> izigaba zesaziso"</string> - <string name="notification_default_channel_desc" msgid="2506053815870808359">"Lolu hlelo lokusebenza alunazo izigaba zesaziso"</string> - <string name="notification_unblockable_desc" msgid="3561016061737896906">"Izaziso kusuka kulolu hlelo lokusebenza azikwazi ukuvalwa"</string> - <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663"> - <item quantity="one">1 isigaba kwezingu-<xliff:g id="NUMBER_1">%s</xliff:g> sezaziso kusukela kulolu hlelo lokusebenza</item> - <item quantity="other">1 isigaba kwezingu-<xliff:g id="NUMBER_1">%s</xliff:g> sezaziso kusukela kulolu hlelo lokusebenza</item> - </plurals> - <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string> - <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157"> - <item quantity="one"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, nabanye abangu-<xliff:g id="NUMBER_5">%3$d</xliff:g></item> - <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g>, nabanye abangu-<xliff:g id="NUMBER_5">%3$d</xliff:g></item> - </plurals> + <string name="notification_channel_disabled" msgid="344536703863700565">"Ngeke usabona lezi zaziso"</string> + <string name="inline_keep_showing" msgid="8945102997083836858">"Qhubeka nokubonisa lezi zaziso?"</string> + <string name="inline_stop_button" msgid="4172980096860941033">"Misa izaziso"</string> + <string name="inline_keep_button" msgid="6665940297019018232">"Qhubeka nokubonisa"</string> + <string name="inline_keep_showing_app" msgid="1723113469580031041">"Qhubeka nokubonisa izaziso kusuka kulolu hlelo lokusebenza?"</string> + <string name="notification_unblockable_desc" msgid="1037434112919403708">"Lezi zaziso azikwazi ukuvalwa"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivuliwe"</string> <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivaliwe"</string> <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"Zonke izaziso kusuka kulesi siteshi"</string> - <string name="notification_all_categories" msgid="5407190218055113282">"Zonke izigaba"</string> <string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string> - <string name="notification_app_settings" msgid="3743278649182392015">"Enza ngendlela oyifisayo: <xliff:g id="SUB_CATEGORY">%1$s</xliff:g>"</string> + <string name="notification_app_settings" msgid="420348114670768449">"Enza ngendlela oyifisayo"</string> <string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string> + <string name="inline_undo" msgid="558916737624706010">"Susa"</string> <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string> <string name="notification_menu_gear_description" msgid="2204480013726775108">"izilawuli zesaziso"</string> <string name="notification_menu_snooze_description" msgid="3653669438131034525">"izinketho zokusnuza zesaziso"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7a670fd1ae5d..4e17c71b0c93 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -215,7 +215,7 @@ <dimen name="close_handle_underlap">32dp</dimen> <!-- Height of the status bar header bar --> - <dimen name="status_bar_header_height">124dp</dimen> + <dimen name="status_bar_header_height">178dp</dimen> <!-- Height of the status bar header bar in the car setting. --> <dimen name="car_status_bar_header_height">128dp</dimen> @@ -223,8 +223,9 @@ <!-- The bottom padding of the status bar header. --> <dimen name="status_bar_header_padding_bottom">48dp</dimen> - <!-- The height of the container that holds the system icons in the quick settings header. --> - <dimen name="qs_header_system_icons_area_height">40dp</dimen> + <!-- The height of the container that holds the battery and time in the quick settings header. + --> + <dimen name="qs_header_system_icons_area_height">48dp</dimen> <!-- The height of the container that holds the system icons in the quick settings header in the car setting. --> @@ -618,6 +619,8 @@ type icon is wide for the tile in quick settings. --> <dimen name="wide_type_icon_start_padding_qs">3dp</dimen> + <dimen name="signal_indicator_to_icon_frame_spacing">3dp</dimen> + <!-- The maximum width of the navigation bar ripples. --> <dimen name="key_button_ripple_max_width">95dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index fed97c57ce09..edda613f2fbc 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -93,5 +93,8 @@ <item type="id" name="action_snooze_long"/> <item type="id" name="action_snooze_longer"/> <item type="id" name="action_snooze_assistant_suggestion_1"/> + + <!-- For StatusBarIconContainer to tag its icon views --> + <item type="id" name="status_bar_view_state_tag" /> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index dde4dcfb23fe..199ccfcaeaa2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -775,6 +775,15 @@ <string name="quick_settings_work_mode_label">Work mode</string> <!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] --> <string name="quick_settings_night_display_label">Night Light</string> + <!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] --> + <string name="quick_settings_night_secondary_label_on_at_sunset">On at sunset</string> + <!-- QuickSettings: Secondary text for when the Night Light will be on until sunrise. [CHAR LIMIT=20] --> + <string name="quick_settings_night_secondary_label_until_sunrise">Until sunrise</string> + <!-- QuickSettings: Secondary text for when the Night Light will be enabled at some user-selected time. [CHAR LIMIT=20] --> + <string name="quick_settings_night_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string> + <!-- QuickSettings: Secondary text for when the Night Light will be on until some user-selected time. [CHAR LIMIT=20] --> + <string name="quick_settings_night_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string> + <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] --> <string name="quick_settings_nfc_label">NFC</string> <!-- QuickSettings: NFC (off) [CHAR LIMIT=NONE] --> @@ -1693,12 +1702,14 @@ <item>Clipboard</item> <item>Keycode</item> <item>Keyboard switcher</item> + <item>Rotation suggestion</item> <item>None</item> </string-array> <string-array name="nav_bar_button_values" translatable="false"> <item>clipboard</item> <item>key</item> <item>menu_ime</item> + <item>rotate</item> <item>space</item> </string-array> @@ -2056,5 +2067,21 @@ <string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings can’t verify your response.</string> + <!-- Title of prompt requesting access to display slices [CHAR LIMIT=NONE] --> + <string name="slice_permission_title">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices?</string> + + <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] --> + <string name="slice_permission_text_1"> - It can read information from <xliff:g id="app" example="Example App">%1$s</xliff:g></string> + <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] --> + <string name="slice_permission_text_2"> - It can take actions inside <xliff:g id="app" example="Example App">%1$s</xliff:g></string> + + <!-- Text on checkbox allowing the app to show slices from all apps [CHAR LIMIT=NONE] --> + <string name="slice_permission_checkbox">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show slices from any app</string> + + <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] --> + <string name="slice_permission_allow">Allow</string> + + <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] --> + <string name="slice_permission_deny">Deny</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 90c5977e2a67..4837fefd04b2 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -476,7 +476,6 @@ <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">sans-serif-medium</item> <item name="android:textSize">14sp</item> - <item name="android:textAllCaps">true</item> <item name="android:textColor">?android:attr/colorAccent</item> <item name="android:background">@drawable/btn_borderless_rect</item> <item name="android:gravity">center</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index a5d19639580e..7d159b745254 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.RectEvaluator; import android.annotation.FloatRange; +import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; @@ -28,14 +29,18 @@ import android.graphics.Color; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; import android.os.Trace; import android.util.ArraySet; import android.util.IntProperty; import android.util.Property; import android.util.TypedValue; +import android.view.Surface; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.ViewRootImpl; import android.view.ViewStub; import java.util.ArrayList; @@ -291,6 +296,28 @@ public class Utilities { } /** + * @return The next frame name for the specified surface or -1 if the surface is no longer + * valid. + */ + public static long getNextFrameNumber(Surface s) { + return s != null && s.isValid() + ? s.getNextFrameNumber() + : -1; + + } + + /** + * @return The surface for the specified view. + */ + public static @Nullable Surface getSurface(View v) { + ViewRootImpl viewRoot = v.getViewRootImpl(); + if (viewRoot == null) { + return null; + } + return viewRoot.mSurface; + } + + /** * Returns a lightweight dump of a rect. */ public static String dumpRect(Rect r) { @@ -299,4 +326,12 @@ public class Utilities { } return r.left + "," + r.top + "-" + r.right + "," + r.bottom; } + + /** + * Posts a runnable on a handler at the front of the queue ignoring any sync barriers. + */ + public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) { + Message msg = h.obtainMessage().setCallback(r); + h.sendMessageAtFrontOfQueue(msg); + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java new file mode 100644 index 000000000000..0d8ce58d55fd --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java @@ -0,0 +1,34 @@ +/* + * 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 com.android.systemui.shared.system; + +import android.app.Activity; + +public class ActivityCompat { + private final Activity mWrapped; + + public ActivityCompat(Activity activity) { + mWrapped = activity; + } + + /** + * @see Activity#registerRemoteAnimations + */ + public void registerRemoteAnimations(RemoteAnimationDefinitionCompat definition) { + mWrapped.registerRemoteAnimations(definition.getWrapped()); + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index 705a21522b0a..712cca67c5d6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -38,4 +38,9 @@ public abstract class ActivityOptionsCompat { : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT); return options; } + + public static ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapterCompat remoteAnimationAdapter) { + return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped()); + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java new file mode 100644 index 000000000000..625b1de74290 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -0,0 +1,71 @@ +/* + * 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 com.android.systemui.shared.system; + +import android.os.RemoteException; +import android.util.Log; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationTarget; + +/** + * @see RemoteAnimationAdapter + */ +public class RemoteAnimationAdapterCompat { + + private final RemoteAnimationAdapter mWrapped; + + public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration, + long statusBarTransitionDelay) { + mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration, + statusBarTransitionDelay); + } + + RemoteAnimationAdapter getWrapped() { + return mWrapped; + } + + private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner( + RemoteAnimationRunnerCompat remoteAnimationAdapter) { + return new IRemoteAnimationRunner.Stub() { + @Override + public void onAnimationStart(RemoteAnimationTarget[] apps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + final RemoteAnimationTargetCompat[] appsCompat = + RemoteAnimationTargetCompat.wrap(apps); + final Runnable animationFinishedCallback = new Runnable() { + @Override + public void run() { + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + + " finished callback", e); + } + } + }; + remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback); + } + + @Override + public void onAnimationCancelled() throws RemoteException { + remoteAnimationAdapter.onAnimationCancelled(); + } + }; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java new file mode 100644 index 000000000000..5fff5febec85 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationDefinitionCompat.java @@ -0,0 +1,35 @@ +/* + * 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 com.android.systemui.shared.system; + +import android.view.RemoteAnimationDefinition; + +/** + * @see RemoteAnimationDefinition + */ +public class RemoteAnimationDefinitionCompat { + + private final RemoteAnimationDefinition mWrapped = new RemoteAnimationDefinition(); + + public void addRemoteAnimation(int transition, RemoteAnimationAdapterCompat adapter) { + mWrapped.addRemoteAnimation(transition, adapter.getWrapped()); + } + + RemoteAnimationDefinition getWrapped() { + return mWrapped; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java new file mode 100644 index 000000000000..5a85df967197 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java @@ -0,0 +1,22 @@ +/* + * 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 com.android.systemui.shared.system; + +public interface RemoteAnimationRunnerCompat { + void onAnimationStart(RemoteAnimationTargetCompat[] apps, Runnable finishedCallback); + void onAnimationCancelled(); +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java new file mode 100644 index 000000000000..3871980a5b17 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -0,0 +1,59 @@ +/* + * 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 com.android.systemui.shared.system; + +import android.graphics.Point; +import android.graphics.Rect; +import android.view.RemoteAnimationTarget; + +/** + * @see RemoteAnimationTarget + */ +public class RemoteAnimationTargetCompat { + + public static final int MODE_OPENING = RemoteAnimationTarget.MODE_OPENING; + public static final int MODE_CLOSING = RemoteAnimationTarget.MODE_CLOSING; + + public final int taskId; + public final int mode; + public final SurfaceControlCompat leash; + public final boolean isTranslucent; + public final Rect clipRect; + public final int prefixOrderIndex; + public final Point position; + public final Rect sourceContainerBounds; + + public RemoteAnimationTargetCompat(RemoteAnimationTarget app) { + taskId = app.taskId; + mode = app.mode; + leash = new SurfaceControlCompat(app.leash); + isTranslucent = app.isTranslucent; + clipRect = app.clipRect; + position = app.position; + sourceContainerBounds = app.sourceContainerBounds; + prefixOrderIndex = app.prefixOrderIndex; + } + + public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) { + final RemoteAnimationTargetCompat[] appsCompat = + new RemoteAnimationTargetCompat[apps.length]; + for (int i = 0; i < apps.length; i++) { + appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]); + } + return appsCompat; + } +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java new file mode 100644 index 000000000000..cd12141c3268 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceControlCompat.java @@ -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 + */ + +package com.android.systemui.shared.system; + +import android.view.SurfaceControl; + +public class SurfaceControlCompat { + SurfaceControl mSurfaceControl; + + public SurfaceControlCompat(SurfaceControl surfaceControl) { + mSurfaceControl = surfaceControl; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java new file mode 100644 index 000000000000..c82c5191b127 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java @@ -0,0 +1,108 @@ +/* + * 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 com.android.systemui.shared.system; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +public class TransactionCompat { + + private final Transaction mTransaction; + + private final float[] mTmpValues = new float[9]; + + public TransactionCompat() { + mTransaction = new Transaction(); + } + + public void apply() { + mTransaction.apply(); + } + + public TransactionCompat show(SurfaceControlCompat surfaceControl) { + mTransaction.show(surfaceControl.mSurfaceControl); + return this; + } + + public TransactionCompat hide(SurfaceControlCompat surfaceControl) { + mTransaction.hide(surfaceControl.mSurfaceControl); + return this; + } + + public TransactionCompat setPosition(SurfaceControlCompat surfaceControl, float x, float y) { + mTransaction.setPosition(surfaceControl.mSurfaceControl, x, y); + return this; + } + + public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) { + mTransaction.setSize(surfaceControl.mSurfaceControl, w, h); + return this; + } + + public TransactionCompat setLayer(SurfaceControlCompat surfaceControl, int z) { + mTransaction.setLayer(surfaceControl.mSurfaceControl, z); + return this; + } + + public TransactionCompat setAlpha(SurfaceControlCompat surfaceControl, float alpha) { + mTransaction.setAlpha(surfaceControl.mSurfaceControl, alpha); + return this; + } + + public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, float dsdx, float dtdx, + float dtdy, float dsdy) { + mTransaction.setMatrix(surfaceControl.mSurfaceControl, dsdx, dtdx, dtdy, dsdy); + return this; + } + + public TransactionCompat setMatrix(SurfaceControlCompat surfaceControl, Matrix matrix) { + mTransaction.setMatrix(surfaceControl.mSurfaceControl, matrix, mTmpValues); + return this; + } + + public TransactionCompat setWindowCrop(SurfaceControlCompat surfaceControl, Rect crop) { + mTransaction.setWindowCrop(surfaceControl.mSurfaceControl, crop); + return this; + } + + public TransactionCompat setFinalCrop(SurfaceControlCompat surfaceControl, Rect crop) { + mTransaction.setFinalCrop(surfaceControl.mSurfaceControl, crop); + return this; + } + + public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl, + IBinder handle, long frameNumber) { + mTransaction.deferTransactionUntil(surfaceControl.mSurfaceControl, handle, frameNumber); + return this; + } + + public TransactionCompat deferTransactionUntil(SurfaceControlCompat surfaceControl, + Surface barrier, long frameNumber) { + mTransaction.deferTransactionUntilSurface(surfaceControl.mSurfaceControl, barrier, + frameNumber); + return this; + } + + public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) { + mTransaction.setColor(surfaceControl.mSurfaceControl, color); + return this; + } +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 225dbb4aafbe..68400fc977df 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -20,9 +20,10 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.graphics.Rect; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.RemoteException; import android.util.Log; +import android.view.RemoteAnimationAdapter; +import android.view.WindowManager; import android.view.WindowManagerGlobal; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; @@ -32,6 +33,31 @@ public class WindowManagerWrapper { private static final String TAG = "WindowManagerWrapper"; + public static final int TRANSIT_UNSET = WindowManager.TRANSIT_UNSET; + public static final int TRANSIT_NONE = WindowManager.TRANSIT_NONE; + public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_ACTIVITY_OPEN; + public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_ACTIVITY_CLOSE; + public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_TASK_OPEN; + public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_TASK_CLOSE; + public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_TASK_TO_FRONT; + public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_TASK_TO_BACK; + public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_WALLPAPER_CLOSE; + public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_WALLPAPER_OPEN; + public static final int TRANSIT_WALLPAPER_INTRA_OPEN = + WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; + public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = + WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; + public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND; + public static final int TRANSIT_TASK_IN_PLACE = WindowManager.TRANSIT_TASK_IN_PLACE; + public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH; + public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = + WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; + public static final int TRANSIT_KEYGUARD_GOING_AWAY = WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; + public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = + WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; + public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE; + public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; + private static final WindowManagerWrapper sInstance = new WindowManagerWrapper(); public static WindowManagerWrapper getInstance() { @@ -65,4 +91,14 @@ public class WindowManagerWrapper { Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e); } } + + public void overridePendingAppTransitionRemote( + RemoteAnimationAdapterCompat remoteAnimationAdapter) { + try { + WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote( + remoteAnimationAdapter.getWrapped()); + } catch (RemoteException e) { + Log.w(TAG, "Failed to override pending app transition (remote): ", e); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index e58ad0589c8a..19afcf5e33a8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -991,6 +991,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { maxChargingWattage > fastThreshold ? CHARGING_FAST : CHARGING_REGULAR; } + + @Override + public String toString() { + return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged + + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}"; + } } public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { @@ -1624,18 +1630,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return true; } - // change in battery level while plugged in - if (nowPluggedIn && old.level != current.level) { - return true; - } - - // change in battery level while keyguard visible - if (mKeyguardIsVisible && old.level != current.level) { - return true; - } - - // change where battery needs charging - if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) { + // change in battery level + if (old.level != current.level) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 2fe66a14a41d..8666b0c873e7 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -224,7 +224,6 @@ public class BatteryMeterView extends LinearLayout implements if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); updatePercentText(); addView(mBatteryPercentView, - 0, new ViewGroup.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index a7d1f0d977c6..0be522bfb8cb 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -51,7 +51,8 @@ import java.util.List; */ public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable { - private static final String TAG = "OverviewProxyService"; + public static final String TAG_OPS = "OverviewProxyService"; + public static final boolean DEBUG_OVERVIEW_PROXY = false; private static final long BACKOFF_MILLIS = 5000; private final Context mContext; @@ -96,12 +97,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis try { service.linkToDeath(mOverviewServiceDeathRcpt, 0); } catch (RemoteException e) { - Log.e(TAG, "Lost connection to launcher service", e); + Log.e(TAG_OPS, "Lost connection to launcher service", e); } try { mOverviewProxy.onBind(mSysUiProxy); } catch (RemoteException e) { - Log.e(TAG, "Failed to call onBind()", e); + Log.e(TAG_OPS, "Failed to call onBind()", e); } notifyConnectionChanged(); } @@ -211,7 +212,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(TAG + " state:"); + pw.println(TAG_OPS + " state:"); pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts); pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController .isCurrentUserSetup()); diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java index 880ae709b59d..f9dbf4a15e5c 100644 --- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java @@ -21,7 +21,7 @@ import android.view.Display; import android.view.View; public interface RecentsComponent { - void showRecentApps(boolean triggeredFromAltTab, boolean fromHome); + void showRecentApps(boolean triggeredFromAltTab); void showNextAffiliatedTask(); void showPrevAffiliatedTask(); diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java new file mode 100644 index 000000000000..302face14c1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java @@ -0,0 +1,88 @@ +/* + * 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 com.android.systemui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.slice.SliceManager; +import android.app.slice.SliceProvider; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.widget.CheckBox; +import android.widget.TextView; + +public class SlicePermissionActivity extends Activity implements OnClickListener, + OnDismissListener { + + private static final String TAG = "SlicePermissionActivity"; + + private CheckBox mAllCheckbox; + + private Uri mUri; + private String mCallingPkg; + private String mProviderPkg; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI); + mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG); + mProviderPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PROVIDER_PKG); + + try { + PackageManager pm = getPackageManager(); + CharSequence app1 = pm.getApplicationInfo(mCallingPkg, 0).loadLabel(pm); + CharSequence app2 = pm.getApplicationInfo(mProviderPkg, 0).loadLabel(pm); + AlertDialog dialog = new AlertDialog.Builder(this) + .setTitle(getString(R.string.slice_permission_title, app1, app2)) + .setView(R.layout.slice_permission_request) + .setNegativeButton(R.string.slice_permission_deny, this) + .setPositiveButton(R.string.slice_permission_allow, this) + .setOnDismissListener(this) + .show(); + TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1); + t1.setText(getString(R.string.slice_permission_text_1, app2)); + TextView t2 = dialog.getWindow().getDecorView().findViewById(R.id.text2); + t2.setText(getString(R.string.slice_permission_text_2, app2)); + mAllCheckbox = dialog.getWindow().getDecorView().findViewById( + R.id.slice_permission_checkbox); + mAllCheckbox.setText(getString(R.string.slice_permission_checkbox, app1)); + } catch (NameNotFoundException e) { + Log.e(TAG, "Couldn't find package", e); + finish(); + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + getSystemService(SliceManager.class).grantPermissionFromUser(mUri, mCallingPkg, + mAllCheckbox.isChecked()); + } + finish(); + } + + @Override + public void onDismiss(DialogInterface dialog) { + finish(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 592dda073d32..a64ce296c76c 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.RectF; @@ -316,10 +317,12 @@ public class SwipeHelper implements Gefingerpoken { float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos; if (Math.abs(delta) > mPagingTouchSlop && Math.abs(delta) > Math.abs(deltaPerpendicular)) { - mCallback.onBeginDrag(mCurrView); - mDragging = true; - mInitialTouchPos = getPos(ev); - mTranslation = getTranslation(mCurrView); + if (mCallback.canChildBeDragged(mCurrView)) { + mCallback.onBeginDrag(mCurrView); + mDragging = true; + mInitialTouchPos = getPos(ev); + mTranslation = getTranslation(mCurrView); + } cancelLongPress(); } } @@ -722,5 +725,10 @@ public class SwipeHelper implements Gefingerpoken { * @return The factor the falsing threshold should be multiplied with */ float getFalsingThresholdFactor(); + + /** + * @return If true, the given view is draggable. + */ + default boolean canChildBeDragged(@NonNull View animView) { return true; } } } diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java index 931a99415615..69e347c9476d 100644 --- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java +++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java @@ -463,4 +463,8 @@ public class DataCollector implements SensorEventListener { public boolean isReportingEnabled() { return mAllowReportRejectedTouch; } + + public void onFalsingSessionStarted() { + sessionEntrypoint(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index e4b405f580d4..ed659e2d16d5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -167,6 +167,9 @@ public class FalsingManager implements SensorEventListener { if (mDataCollector.isEnabledFull()) { registerSensors(COLLECTOR_SENSORS); } + if (mDataCollector.isEnabled()) { + mDataCollector.onFalsingSessionStarted(); + } } private void registerSensors(int [] sensors) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 653e5000f72c..8501519d26aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -707,6 +707,10 @@ public class KeyguardViewMediator extends SystemUI { && !mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser()), mSecondaryDisplayShowing, true /* forceCallbacks */); + } else { + // The system's keyguard is disabled or missing. + setShowingLocked(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()), + mSecondaryDisplayShowing, true); } mStatusBarKeyguardViewManager = diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 7f0acc254a7c..7320b861dbd5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -45,8 +45,9 @@ public class QSContainerImpl extends FrameLayout { protected float mQsExpansion; private QSCustomizer mQSCustomizer; private View mQSFooter; - private float mFullElevation; + private View mBackground; private float mRadius; + private int mSideMargins; public QSContainerImpl(Context context, AttributeSet attrs) { super(context, attrs); @@ -60,12 +61,14 @@ public class QSContainerImpl extends FrameLayout { mHeader = findViewById(R.id.header); mQSCustomizer = findViewById(R.id.qs_customize); mQSFooter = findViewById(R.id.qs_footer); - mFullElevation = mQSPanel.getElevation(); + mBackground = findViewById(R.id.quick_settings_background); mRadius = getResources().getDimensionPixelSize( Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); + mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); setClickable(true); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + setMargins(); } @Override @@ -131,6 +134,8 @@ public class QSContainerImpl extends FrameLayout { mQSDetail.setBottom(getTop() + height); // Pin QS Footer to the bottom of the panel. mQSFooter.setTranslationY(height - mQSFooter.getHeight()); + mBackground.setTop(mQSPanel.getTop()); + mBackground.setBottom(height); ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius, mRadius, @@ -148,4 +153,19 @@ public class QSContainerImpl extends FrameLayout { mQsExpansion = expansion; updateExpansion(); } + + private void setMargins() { + setMargins(mQSDetail); + setMargins(mBackground); + setMargins(mQSFooter); + setMargins(mQSPanel); + setMargins(mHeader); + setMargins(mQSCustomizer); + } + + private void setMargins(View view) { + FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams(); + lp.rightMargin = mSideMargins; + lp.leftMargin = mSideMargins; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 927a49cb60f3..92475da697bd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -61,18 +61,16 @@ import com.android.systemui.tuner.TunerService; public class QSFooterImpl extends FrameLayout implements QSFooter, OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback, CommandQueue.Callbacks { - private static final float EXPAND_INDICATOR_THRESHOLD = .93f; - private ActivityStarter mActivityStarter; private UserInfoController mUserInfoController; private SettingsButton mSettingsButton; protected View mSettingsContainer; + private View mCarrierText; private boolean mQsDisabled; private QSPanel mQsPanel; private boolean mExpanded; - protected ExpandableIndicator mExpandIndicator; private boolean mListening; @@ -100,18 +98,18 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> mQsPanel.showEdit(view))); - mExpandIndicator = findViewById(R.id.expand_indicator); mSettingsButton = findViewById(R.id.settings_button); mSettingsContainer = findViewById(R.id.settings_button_container); mSettingsButton.setOnClickListener(this); + mCarrierText = findViewById(R.id.qs_carrier_text); + mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar); // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true); - ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true); updateResources(); @@ -162,6 +160,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, return new TouchAnimator.Builder() .addFloat(mEdit, "alpha", 0, 1) .addFloat(mMultiUserSwitch, "alpha", 0, 1) + .addFloat(mCarrierText, "alpha", 0, 1) + .addFloat(mSettingsButton, "alpha", 0, 1) .build(); } @@ -185,8 +185,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, if (mSettingsAlpha != null) { mSettingsAlpha.setPosition(headerExpansionFraction); } - - mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD); } @Override @@ -237,8 +235,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE); - mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); - final boolean isDemo = UserManager.isDeviceInDemoMode(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 398592ad1699..e6fd2f4697dd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -38,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSDetail.Callback; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.SignalClusterView; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks { @@ -69,15 +70,13 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue updateResources(); - // Set the light/dark theming on the header status UI to match the current theme. + // Set light text on the header icons because they will always be on a black background int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground); - float intensity = colorForeground == Color.WHITE ? 0 : 1; Rect tintArea = new Rect(0, 0, 0, 0); - - applyDarkness(R.id.battery, tintArea, intensity, colorForeground); - applyDarkness(R.id.clock, tintArea, intensity, colorForeground); + applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT); BatteryMeterView battery = findViewById(R.id.battery); + battery.setFillColor(Color.WHITE); battery.setForceShowPercent(true); mActivityStarter = Dependency.get(ActivityStarter.class); diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java index 9ee40ccf8893..d9583af65df6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java @@ -41,6 +41,7 @@ public class SignalTileView extends QSIconViewImpl { private ImageView mOut; private int mWideOverlayIconStartPadding; + private int mSignalIndicatorToIconFrameSpacing; public SignalTileView(Context context) { super(context); @@ -48,8 +49,13 @@ public class SignalTileView extends QSIconViewImpl { mIn = addTrafficView(R.drawable.ic_qs_signal_in); mOut = addTrafficView(R.drawable.ic_qs_signal_out); + setClipChildren(false); + setClipToPadding(false); + mWideOverlayIconStartPadding = context.getResources().getDimensionPixelSize( R.dimen.wide_type_icon_start_padding_qs); + mSignalIndicatorToIconFrameSpacing = context.getResources().getDimensionPixelSize( + R.dimen.signal_indicator_to_icon_frame_spacing); } private ImageView addTrafficView(int icon) { @@ -99,10 +105,10 @@ public class SignalTileView extends QSIconViewImpl { boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; int left, right; if (isRtl) { - right = mIconFrame.getLeft(); + right = getLeft() - mSignalIndicatorToIconFrameSpacing; left = right - indicator.getMeasuredWidth(); } else { - left = mIconFrame.getRight(); + left = getRight() + mSignalIndicatorToIconFrameSpacing; right = left + indicator.getMeasuredWidth(); } indicator.layout( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index acd327b8eae2..b4cfda60fba3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -85,6 +85,8 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(0, padding, 0, padding); mIconFrame.addView(mIcon, params); + mIconFrame.setClipChildren(false); + mIconFrame.setClipToPadding(false); mTileBackground = newTileBackground(); if (mTileBackground instanceof RippleDrawable) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index a226f3cac83a..9eb9906ba3b5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -29,7 +29,6 @@ import android.widget.TextView; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; -import com.android.systemui.R.id; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 763ffc677bc2..99a9be38065d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Intent; import android.provider.Settings; @@ -29,9 +30,17 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.tileimpl.QSTileImpl; +import java.time.format.DateTimeFormatter; + public class NightDisplayTile extends QSTileImpl<BooleanState> implements ColorDisplayController.Callback { + /** + * Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the + * nearest hour and add on the AM/PM indicator. + */ + private static final String APPROXIMATE_HOUR_DATE_TIME_PATTERN = "H a"; + private ColorDisplayController mController; private boolean mIsListening; @@ -74,13 +83,49 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> @Override protected void handleUpdateState(BooleanState state, Object arg) { - final boolean isActivated = mController.isActivated(); - state.value = isActivated; + state.value = mController.isActivated(); state.label = state.contentDescription = mContext.getString(R.string.quick_settings_night_display_label); state.icon = ResourceIcon.get(R.drawable.ic_qs_night_display_on); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.secondaryLabel = getSecondaryLabel(state.value); + } + + /** + * Returns a {@link String} for the secondary label that reflects when the light will be turned + * on or off based on the current auto mode and night light activated status. + */ + @Nullable + private String getSecondaryLabel(boolean isNightLightActivated) { + switch(mController.getAutoMode()) { + case ColorDisplayController.AUTO_MODE_TWILIGHT: + // Auto mode related to sunrise & sunset. If the light is on, it's guaranteed to be + // turned off at sunrise. If it's off, it's guaranteed to be turned on at sunset. + return isNightLightActivated + ? mContext.getString( + R.string.quick_settings_night_secondary_label_until_sunrise) + : mContext.getString( + R.string.quick_settings_night_secondary_label_on_at_sunset); + + case ColorDisplayController.AUTO_MODE_CUSTOM: + // User-specified time, approximated to the nearest hour. + return isNightLightActivated + ? mContext.getString( + R.string.quick_settings_night_secondary_label_until, + mController.getCustomEndTime().format( + DateTimeFormatter.ofPattern( + APPROXIMATE_HOUR_DATE_TIME_PATTERN))) + : mContext.getString( + R.string.quick_settings_night_secondary_label_on_at, + mController.getCustomStartTime().format( + DateTimeFormatter.ofPattern( + APPROXIMATE_HOUR_DATE_TIME_PATTERN))); + + default: + // No secondary label when auto mode is disabled. + return null; + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 1e00894483ac..60422ee61aa5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -36,20 +36,8 @@ import com.android.systemui.statusbar.policy.RotationLockController.RotationLock /** Quick settings tile: Rotation **/ public class RotationLockTile extends QSTileImpl<BooleanState> { - private final AnimationIcon mPortraitToAuto - = new AnimationIcon(R.drawable.ic_portrait_to_auto_rotate_animation, - R.drawable.ic_portrait_from_auto_rotate); - private final AnimationIcon mAutoToPortrait - = new AnimationIcon(R.drawable.ic_portrait_from_auto_rotate_animation, - R.drawable.ic_portrait_to_auto_rotate); - - private final AnimationIcon mLandscapeToAuto - = new AnimationIcon(R.drawable.ic_landscape_to_auto_rotate_animation, - R.drawable.ic_landscape_from_auto_rotate); - private final AnimationIcon mAutoToLandscape - = new AnimationIcon(R.drawable.ic_landscape_from_auto_rotate_animation, - R.drawable.ic_landscape_to_auto_rotate); + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_auto_rotate); private final RotationLockController mController; public RotationLockTile(QSHost host) { @@ -93,19 +81,10 @@ public class RotationLockTile extends QSTileImpl<BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { if (mController == null) return; final boolean rotationLocked = mController.isRotationLocked(); - // TODO: Handle accessibility rotation lock and whatnot. state.value = !rotationLocked; - final boolean portrait = isCurrentOrientationLockPortrait(mController, mContext); - if (rotationLocked) { - final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label - : R.string.quick_settings_rotation_locked_landscape_label; - state.label = mContext.getString(label); - state.icon = portrait ? mAutoToPortrait : mAutoToLandscape; - } else { - state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); - state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto; - } + state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); + state.icon = mIcon; state.contentDescription = getAccessibilityString(rotationLocked); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; @@ -134,18 +113,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> { * @param locked Whether or not rotation is locked. */ private String getAccessibilityString(boolean locked) { - if (locked) { - return mContext.getString(R.string.accessibility_quick_settings_rotation_value, - isCurrentOrientationLockPortrait(mController, mContext) - ? mContext.getString( - R.string.quick_settings_rotation_locked_portrait_label) - : mContext.getString( - R.string.quick_settings_rotation_locked_landscape_label)) - + "," + mContext.getString(R.string.accessibility_quick_settings_rotation); - - } else { - return mContext.getString(R.string.accessibility_quick_settings_rotation); - } + return mContext.getString(R.string.accessibility_quick_settings_rotation); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl index 5ae7f22c4905..fc1831d55c9d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl @@ -27,7 +27,7 @@ oneway interface IRecentsNonSystemUserCallbacks { void preloadRecents(); void cancelPreloadingRecents(); void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate, - boolean reloadTasks, boolean fromHome, int recentsGrowTarget); + int recentsGrowTarget); void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); void toggleRecents(int recentsGrowTarget); void onConfigurationChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 5b62c7d3c002..1da4deb61176 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -240,7 +240,7 @@ public class Recents extends SystemUI * Shows the Recents. */ @Override - public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { + public void showRecentApps(boolean triggeredFromAltTab) { // Ensure the device has been provisioned before allowing the user to interact with // recents if (!isUserSetup()) { @@ -252,7 +252,7 @@ public class Recents extends SystemUI int currentUser = sSystemServicesProxy.getCurrentUser(); if (sSystemServicesProxy.isSystemUser(currentUser)) { mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */, - true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget); + true /* animate */, recentsGrowTarget); } else { if (mSystemToUserCallbacks != null) { IRecentsNonSystemUserCallbacks callbacks = @@ -260,8 +260,7 @@ public class Recents extends SystemUI if (callbacks != null) { try { callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */, - true /* animate */, false /* reloadTasks */, fromHome, - recentsGrowTarget); + true /* animate */, recentsGrowTarget); } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 06dfd183b3aa..b0a2fadf1f84 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -356,15 +356,15 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD registerReceiver(mSystemBroadcastReceiver, filter); getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION); - - // Reload the stack view - reloadStackView(); } @Override protected void onStart() { super.onStart(); + // Reload the stack view whenever we are made visible again + reloadStackView(); + // Notify that recents is now visible EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true)); MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY); @@ -411,14 +411,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - // Reload the stack view - reloadStackView(); - } - /** * Reloads the stack views upon launching Recents. */ @@ -530,7 +522,11 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD // Set the window background mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode); - reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */); + // Reload the task stack view if we are still visible to pick up the change in tasks that + // result from entering/exiting multi-window + if (mIsVisible) { + reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 8359690b4fe3..ee1b09109d38 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -255,7 +255,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // When this fires, then the user has not released alt-tab for at least // FAST_ALT_TAB_DELAY_MS milliseconds showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */, - false /* reloadTasks */, false /* fromHome */, DividerView.INVALID_RECENTS_GROW_TARGET); } }); @@ -322,8 +321,15 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, - boolean animate, boolean launchedWhileDockingTask, boolean fromHome, - int growTarget) { + boolean animate, int growTarget) { + final SystemServicesProxy ssp = Recents.getSystemServices(); + final MutableBoolean isHomeStackVisible = new MutableBoolean(true); + final boolean isRecentsVisible = Recents.getSystemServices().isRecentsActivityVisible( + isHomeStackVisible); + final boolean fromHome = isHomeStackVisible.value; + final boolean launchedWhileDockingTask = + Recents.getSystemServices().getSplitScreenPrimaryStack() != null; + mTriggeredFromAltTab = triggeredFromAltTab; mDraggingInRecents = draggingInRecents; mLaunchedWhileDocking = launchedWhileDockingTask; @@ -349,10 +355,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener try { // Check if the top task is in the home stack, and start the recents activity - SystemServicesProxy ssp = Recents.getSystemServices(); - boolean forceVisible = launchedWhileDockingTask || draggingInRecents; - MutableBoolean isHomeStackVisible = new MutableBoolean(forceVisible); - if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) { + final boolean forceVisible = launchedWhileDockingTask || draggingInRecents; + if (forceVisible || !isRecentsVisible) { ActivityManager.RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask(); startRecentsActivityAndDismissKeyguardIfNeeded(runningTask, diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java index 9493c78f6278..beec4b395e9c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java @@ -58,15 +58,12 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub { @Override public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate, - boolean reloadTasks, boolean fromHome, int growTarget) - throws RemoteException { + int growTarget) throws RemoteException { SomeArgs args = SomeArgs.obtain(); args.argi1 = triggeredFromAltTab ? 1 : 0; args.argi2 = draggingInRecents ? 1 : 0; args.argi3 = animate ? 1 : 0; - args.argi4 = reloadTasks ? 1 : 0; - args.argi5 = fromHome ? 1 : 0; - args.argi6 = growTarget; + args.argi4 = growTarget; mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_RECENTS, args)); } @@ -130,7 +127,7 @@ public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub { case MSG_SHOW_RECENTS: args = (SomeArgs) msg.obj; mImpl.showRecents(args.argi1 != 0, args.argi2 != 0, args.argi3 != 0, - args.argi4 != 0, args.argi5 != 0, args.argi6); + args.argi4); break; case MSG_HIDE_RECENTS: mImpl.hideRecents(msg.arg1 != 0, msg.arg2 != 0); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 130a5e310fd1..613d9fbb985c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -274,20 +274,21 @@ public class SystemServicesProxy { return false; } + public ActivityManager.StackInfo getSplitScreenPrimaryStack() { + try { + return mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); + } catch (RemoteException e) { + return null; + } + } + /** * @return whether there are any docked tasks for the current user. */ public boolean hasDockedTask() { if (mIam == null) return false; - ActivityManager.StackInfo stackInfo = null; - try { - stackInfo = - mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); - } catch (RemoteException e) { - e.printStackTrace(); - } - + ActivityManager.StackInfo stackInfo = getSplitScreenPrimaryStack(); if (stackInfo != null) { int userId = getCurrentUser(); boolean hasUserTask = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 36c9095fe8ae..5be2900831b3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -210,7 +210,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal private boolean mStackActionButtonVisible; // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes - private float mLastScrollPPercent; + private float mLastScrollPPercent = -1; // We keep track of the task view focused by user interaction and draw a frame around it in the // grid layout. @@ -647,14 +647,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * an animation provided in {@param animationOverrides}, that will be used instead. */ private void relayoutTaskViews(AnimationProps animation, - ArrayMap<Task, AnimationProps> animationOverrides, - boolean ignoreTaskOverrides) { + ArrayMap<Task, AnimationProps> animationOverrides, boolean ignoreTaskOverrides) { // If we had a deferred animation, cancel that cancelDeferredTaskViewLayoutAnimation(); // Synchronize the current set of TaskViews - bindVisibleTaskViews(mStackScroller.getStackScroll(), - ignoreTaskOverrides /* ignoreTaskOverrides */); + bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides); // Animate them to their final transforms with the given animation List<TaskView> taskViews = getTaskViews(); @@ -2067,8 +2065,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Update the Clear All button in case we're switching in or out of grid layout. updateStackActionButtonVisibility(); - // Trigger a new layout and update to the initial state if necessary - if (event.fromMultiWindow) { + // Trigger a new layout and update to the initial state if necessary. When entering split + // screen, the multi-window configuration change event can happen after the stack is already + // reloaded (but pending measure/layout), in this case, do not override the intiial state + // and just wait for the upcoming measure/layout pass. + if (event.fromMultiWindow && mInitialState == INITIAL_STATE_UPDATE_NONE) { mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY; requestLayout(); } else if (event.fromDeviceOrientationChange) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 6db46b5917b6..675aa8f73a09 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -293,12 +293,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - // Create a share action for the notification. Note, we proxy the call to ShareReceiver - // because RemoteViews currently forces an activity options on the PendingIntent being - // launched, and since we don't want to trigger the share sheet in this case, we will - // start the chooser activitiy directly in ShareReceiver. + // Create a share action for the notification. Note, we proxy the call to + // ScreenshotActionReceiver because RemoteViews currently forces an activity options + // on the PendingIntent being launched, and since we don't want to trigger the share + // sheet in this case, we start the chooser activity directly in + // ScreenshotActionReceiver. PendingIntent shareAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.ShareReceiver.class) + new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class) .putExtra(SHARING_INTENT, sharingIntent), PendingIntent.FLAG_CANCEL_CURRENT); Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( @@ -306,15 +307,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { r.getString(com.android.internal.R.string.share), shareAction); mNotificationBuilder.addAction(shareActionBuilder.build()); - // Create a delete action for the notification - PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) - .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( - R.drawable.ic_screenshot_delete, - r.getString(com.android.internal.R.string.delete), deleteAction); - mNotificationBuilder.addAction(deleteActionBuilder.build()); + Intent editIntent = new Intent(Intent.ACTION_EDIT); + editIntent.setType("image/png"); + editIntent.putExtra(Intent.EXTRA_STREAM, uri); + + // Create a edit action for the notification the same way. + PendingIntent editAction = PendingIntent.getBroadcast(context, 1, + new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class) + .putExtra(SHARING_INTENT, editIntent), + PendingIntent.FLAG_CANCEL_CURRENT); + Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( + R.drawable.ic_screenshot_edit, + r.getString(com.android.internal.R.string.screenshot_edit), editAction); + mNotificationBuilder.addAction(editActionBuilder.build()); mParams.imageUri = uri; mParams.image = null; @@ -879,9 +884,9 @@ class GlobalScreenshot { } /** - * Receiver to proxy the share intent. + * Receiver to proxy the share or edit intent. */ - public static class ShareReceiver extends BroadcastReceiver { + public static class ScreenshotActionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { @@ -903,7 +908,7 @@ class GlobalScreenshot { } /** - * Removes the notification for a screenshot after a share target is chosen. + * Removes the notification for a screenshot after a share or edit target is chosen. */ public static class TargetChosenReceiver extends BroadcastReceiver { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 8e1b10432a3d..c6abcf272722 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -116,7 +116,7 @@ public class CommandQueue extends IStatusBar.Stub { default void topAppWindowChanged(boolean visible) { } default void setImeWindowStatus(IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { } - default void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { } + default void showRecentApps(boolean triggeredFromAltTab) { } default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { } default void toggleRecentApps() { } default void toggleSplitScreen() { } @@ -144,7 +144,7 @@ public class CommandQueue extends IStatusBar.Stub { default void handleShowGlobalActionsMenu() { } default void handleShowShutdownUi(boolean isReboot, String reason) { } - default void onRotationProposal(int rotation) { } + default void onRotationProposal(int rotation, boolean isValid) { } } @VisibleForTesting @@ -268,11 +268,11 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { + public void showRecentApps(boolean triggeredFromAltTab) { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_RECENT_APPS); - mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, - triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0, null).sendToTarget(); + mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0, 0, + null).sendToTarget(); } } @@ -462,10 +462,10 @@ public class CommandQueue extends IStatusBar.Stub { } @Override - public void onProposedRotationChanged(int rotation) { + public void onProposedRotationChanged(int rotation, boolean isValid) { synchronized (mLock) { mHandler.removeMessages(MSG_ROTATION_PROPOSAL); - mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0, + mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, isValid ? 1 : 0, null).sendToTarget(); } } @@ -541,7 +541,7 @@ public class CommandQueue extends IStatusBar.Stub { break; case MSG_SHOW_RECENT_APPS: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).showRecentApps(msg.arg1 != 0, msg.arg2 != 0); + mCallbacks.get(i).showRecentApps(msg.arg1 != 0); } break; case MSG_HIDE_RECENT_APPS: @@ -668,7 +668,7 @@ public class CommandQueue extends IStatusBar.Stub { break; case MSG_ROTATION_PROPOSAL: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onRotationProposal(msg.arg1); + mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0); } break; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index bf8a64cee974..5f4854aeeb7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -2377,7 +2377,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView NotificationContentView contentView = (NotificationContentView) child; if (isClippingNeeded()) { return true; - } else if (!hasNoRounding() && contentView.shouldClipToRounding()) { + } else if (!hasNoRounding() + && contentView.shouldClipToRounding(getCurrentTopRoundness() != 0.0f, + getCurrentBottomRoundness() != 0.0f)) { return true; } } else if (child == mChildrenContainer) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 43047ed6a5c5..0a12be4eac7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -52,6 +52,8 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.text.NumberFormat; /** @@ -116,11 +118,9 @@ public class KeyguardIndicationController { WakeLock wakeLock) { mContext = context; mIndicationArea = indicationArea; - mTextView = (KeyguardIndicationTextView) indicationArea.findViewById( - R.id.keyguard_indication_text); + mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); mInitialTextColor = mTextView != null ? mTextView.getCurrentTextColor() : Color.WHITE; - mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById( - R.id.keyguard_indication_enterprise_disclosure); + mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); mLockIcon = lockIcon; mWakeLock = new SettableWakeLock(wakeLock); @@ -416,6 +416,21 @@ public class KeyguardIndicationController { updateDisclosure(); } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyguardIndicationController:"); + pw.println(" mTransientTextColor: " + Integer.toHexString(mTransientTextColor)); + pw.println(" mInitialTextColor: " + Integer.toHexString(mInitialTextColor)); + pw.println(" mPowerPluggedIn: " + mPowerPluggedIn); + pw.println(" mPowerCharged: " + mPowerCharged); + pw.println(" mChargingSpeed: " + mChargingSpeed); + pw.println(" mChargingWattage: " + mChargingWattage); + pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); + pw.println(" mDozing: " + mDozing); + pw.println(" mBatteryLevel: " + mBatteryLevel); + pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); + pw.println(" computePowerIndication(): " + computePowerIndication()); + } + protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; private int mLastSuccessiveErrorMessage = -1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index c73e548eaa17..64df92c3bd51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -1489,19 +1489,21 @@ public class NotificationContentView extends FrameLayout { return false; } - public boolean shouldClipToRounding() { - boolean needsPaddings = shouldClipToRounding(getVisibleType()); + public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { + boolean needsPaddings = shouldClipToRounding(getVisibleType(), topRounded, bottomRounded); if (mUserExpanding) { - needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType); + needsPaddings |= shouldClipToRounding(mTransformationStartVisibleType, topRounded, + bottomRounded); } return needsPaddings; } - private boolean shouldClipToRounding(int visibleType) { + private boolean shouldClipToRounding(int visibleType, boolean topRounded, + boolean bottomRounded) { NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType); if (visibleWrapper == null) { return false; } - return visibleWrapper.shouldClipToRounding(); + return visibleWrapper.shouldClipToRounding(topRounded, bottomRounded); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 7e0dba5e129d..127f3f918fba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -67,6 +67,7 @@ public class NotificationData { public static final class Entry { private static final long LAUNCH_COOLDOWN = 2000; + private static final long REMOTE_INPUT_COOLDOWN = 500; private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; private static final int COLOR_INVALID = 1; public String key; @@ -92,6 +93,8 @@ public class NotificationData { private int mCachedContrastColorIsFor = COLOR_INVALID; private InflationTask mRunningTask = null; private Throwable mDebugThrowable; + public CharSequence remoteInputTextWhenReset; + public long lastRemoteInputSent = NOT_LAUNCHED_YET; public Entry(StatusBarNotification n) { this.key = n.getKey(); @@ -132,6 +135,10 @@ public class NotificationData { return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN; } + public boolean hasJustSentRemoteInput() { + return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN; + } + /** * Create the icons for a notification * @param context the context to create the icons with @@ -265,6 +272,11 @@ public class NotificationData { public Throwable getDebugThrowable() { return mDebugThrowable; } + + public void onRemoteInputInserted() { + lastRemoteInputSent = NOT_LAUNCHED_YET; + remoteInputTextWhenReset = null; + } } private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java index 6bbd09f715db..7360486ac7e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java @@ -35,6 +35,7 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; +import android.text.TextUtils; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; @@ -462,7 +463,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mMediaManager.onNotificationRemoved(key); NotificationData.Entry entry = mNotificationData.get(key); - if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputManager.getController().isSpinning(key) + if (FORCE_REMOTE_INPUT_HISTORY + && shouldKeepForRemoteInput(entry) && entry.row != null && !entry.row.isDismissed()) { StatusBarNotification sbn = entry.notification; @@ -477,7 +479,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. newHistory = new CharSequence[oldHistory.length + 1]; System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length); } - newHistory[0] = String.valueOf(entry.remoteInputText); + CharSequence remoteInputText = entry.remoteInputText; + if (TextUtils.isEmpty(remoteInputText)) { + remoteInputText = entry.remoteInputTextWhenReset; + } + newHistory[0] = String.valueOf(remoteInputText); b.setRemoteInputHistory(newHistory); Notification newNotification = b.build(); @@ -492,6 +498,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); boolean updated = false; + entry.onRemoteInputInserted(); try { updateNotificationInternal(newSbn, null); updated = true; @@ -539,6 +546,19 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. mCallback.onNotificationRemoved(key, old); } + private boolean shouldKeepForRemoteInput(NotificationData.Entry entry) { + if (entry == null) { + return false; + } + if (mRemoteInputManager.getController().isSpinning(entry.key)) { + return true; + } + if (entry.hasJustSentRemoteInput()) { + return true; + } + return false; + } + private StatusBarNotification removeNotificationViews(String key, NotificationListenerService.RankingMap ranking) { NotificationData.Entry entry = mNotificationData.remove(key, ranking); @@ -596,8 +616,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. && entry.row.getGuts() == mGutsManager.getExposedGuts(); entry.row.onDensityOrFontScaleChanged(); if (exposedGuts) { - mGutsManager.setExposedGuts(entry.row.getGuts()); - mGutsManager.bindGuts(entry.row); + mGutsManager.onDensityOrFontScaleChanged(entry.row); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index c4024a57ceed..52776d7e8430 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -23,6 +23,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.view.ViewAnimationUtils; @@ -187,6 +188,12 @@ public class NotificationGuts extends FrameLayout { } } + public void openControls( + int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) { + animateOpen(x, y, onAnimationEnd); + setExposed(true /* exposed */, needsFalsingProtection); + } + public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) { if (mGutsContent != null) { if (mGutsContent.isLeavebehind() && leavebehinds) { @@ -214,6 +221,27 @@ public class NotificationGuts extends FrameLayout { } } + private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) { + final double horz = Math.max(getWidth() - x, x); + final double vert = Math.max(getHeight() - y, y); + final float r = (float) Math.hypot(horz, vert); + + final Animator a + = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r); + a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + a.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (onAnimationEnd != null) { + onAnimationEnd.run(); + } + } + }); + a.start(); + } + private void animateClose(int x, int y) { if (x == -1 || y == -1) { x = (getLeft() + getRight()) / 2; @@ -279,7 +307,7 @@ public class NotificationGuts extends FrameLayout { } } - public void setExposed(boolean exposed, boolean needsFalsingProtection) { + private void setExposed(boolean exposed, boolean needsFalsingProtection) { final boolean wasExposed = mExposed; mExposed = exposed; mNeedsFalsingProtection = needsFalsingProtection; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 441c18431894..9d8892da3c74 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -15,8 +15,6 @@ */ package com.android.systemui.statusbar; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.app.INotificationManager; import android.app.NotificationChannel; import android.content.Context; @@ -32,17 +30,14 @@ import android.util.ArraySet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; -import com.android.systemui.Interpolators; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.stack.StackStateAnimator; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -112,6 +107,11 @@ public class NotificationGutsManager implements Dumpable { mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed; } + public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) { + setExposedGuts(row.getGuts()); + bindGuts(row); + } + private void saveAndCloseNotificationMenu( ExpandableNotificationRow row, NotificationGuts guts, View done) { guts.resetFalsingCheck(); @@ -270,7 +270,7 @@ public class NotificationGutsManager implements Dumpable { } /** - * Opens guts on the given ExpandableNotificationRow |v|. + * Opens guts on the given ExpandableNotificationRow |v|. * * @param v ExpandableNotificationRow to open guts on * @param x x coordinate of origin of circular reveal @@ -326,26 +326,15 @@ public class NotificationGutsManager implements Dumpable { true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */); guts.setVisibility(View.VISIBLE); - final double horz = Math.max(guts.getWidth() - x, x); - final double vert = Math.max(guts.getHeight() - y, y); - final float r = (float) Math.hypot(horz, vert); - final Animator a - = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r); - a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); - a.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - // Move the notification view back over the menu - row.resetTranslation(); - } - }); - a.start(); + final boolean needsFalsingProtection = (mPresenter.isPresenterLocked() && !mAccessibilityManager.isTouchExplorationEnabled()); - guts.setExposed(true /* exposed */, needsFalsingProtection); + guts.openControls(x, y, needsFalsingProtection, () -> { + // Move the notification view back over the menu + row.resetTranslation(); + }); + row.closeRemoteInput(); mListContainer.onHeightChanged(row, true /* needsAnimation */); mNotificationGutsExposed = guts; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index 6cbbd6cd1f18..e5a311d099d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -17,11 +17,15 @@ package com.android.systemui.statusbar.car; import android.content.Context; +import android.graphics.Canvas; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.phone.NavGesture; +import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import com.android.systemui.statusbar.phone.NavigationBarView; /** @@ -72,4 +76,68 @@ class CarNavigationBarView extends NavigationBarView { // Calling setNavigationIconHints in the base class will result in a NPE as the car // navigation bar does not have a back button. } + + @Override + public void onPluginConnected(NavGesture plugin, Context context) { + // set to null version of the plugin ignoring incoming arg. + super.onPluginConnected(new NullNavGesture(), context); + } + + @Override + public void onPluginDisconnected(NavGesture plugin) { + // reinstall the null nav gesture plugin + super.onPluginConnected(new NullNavGesture(), getContext()); + } + + /** + * Null object pattern to work around expectations of the base class. + * This is a temporary solution to have the car system ui working. + * Already underway is a refactor of they car sys ui as to not use this class + * hierarchy. + */ + private static class NullNavGesture implements NavGesture { + @Override + public GestureHelper getGestureHelper() { + return new GestureHelper() { + @Override + public boolean onTouchEvent(MotionEvent event) { + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + return false; + } + + @Override + public void setBarState(boolean vertical, boolean isRtl) { + } + + @Override + public void onDraw(Canvas canvas) { + } + + @Override + public void onDarkIntensityChange(float intensity) { + } + + @Override + public void onLayout(boolean changed, int left, int top, int right, int bottom) { + } + }; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public void onCreate(Context sysuiContext, Context pluginContext) { + } + + @Override + public void onDestroy() { + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java index 27defcace7d8..113118a1c8c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java @@ -26,9 +26,7 @@ import com.android.internal.widget.MessagingLayout; import com.android.internal.widget.MessagingLinearLayout; import com.android.internal.widget.MessagingMessage; import com.android.internal.widget.MessagingPropertyAnimator; -import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.Interpolators; -import com.android.systemui.statusbar.ExpandableNotificationRow; import java.util.ArrayList; import java.util.HashMap; @@ -157,8 +155,8 @@ public class MessagingLayoutTransformState extends TransformState { setClippingDeactivated(child, true); } appear(ownGroup.getAvatar(), transformationAmount); - appear(ownGroup.getSender(), transformationAmount); - setClippingDeactivated(ownGroup.getSender(), true); + appear(ownGroup.getSenderView(), transformationAmount); + setClippingDeactivated(ownGroup.getSenderView(), true); setClippingDeactivated(ownGroup.getAvatar(), true); } @@ -170,7 +168,7 @@ public class MessagingLayoutTransformState extends TransformState { } else { relativeOffset = (1.0f - transformationAmount) * mRelativeTranslationOffset; } - if (ownGroup.getSender().getVisibility() != View.GONE) { + if (ownGroup.getSenderView().getVisibility() != View.GONE) { relativeOffset *= 0.5f; } ownGroup.getMessageContainer().setTranslationY(relativeOffset); @@ -188,8 +186,8 @@ public class MessagingLayoutTransformState extends TransformState { setClippingDeactivated(child, true); } disappear(ownGroup.getAvatar(), transformationAmount); - disappear(ownGroup.getSender(), transformationAmount); - setClippingDeactivated(ownGroup.getSender(), true); + disappear(ownGroup.getSenderView(), transformationAmount); + setClippingDeactivated(ownGroup.getSenderView(), true); setClippingDeactivated(ownGroup.getAvatar(), true); } @@ -226,7 +224,7 @@ public class MessagingLayoutTransformState extends TransformState { private void transformGroups(MessagingGroup ownGroup, MessagingGroup otherGroup, float transformationAmount, boolean to) { - transformView(transformationAmount, to, ownGroup.getSender(), otherGroup.getSender(), + transformView(transformationAmount, to, ownGroup.getSenderView(), otherGroup.getSenderView(), true /* sameAsAny */); transformView(transformationAmount, to, ownGroup.getAvatar(), otherGroup.getAvatar(), true /* sameAsAny */); @@ -345,7 +343,7 @@ public class MessagingLayoutTransformState extends TransformState { setVisible(child, visible, force); } setVisible(ownGroup.getAvatar(), visible, force); - setVisible(ownGroup.getSender(), visible, force); + setVisible(ownGroup.getSenderView(), visible, force); } } } @@ -376,9 +374,9 @@ public class MessagingLayoutTransformState extends TransformState { setClippingDeactivated(child, false); } resetTransformedView(ownGroup.getAvatar()); - resetTransformedView(ownGroup.getSender()); + resetTransformedView(ownGroup.getSenderView()); setClippingDeactivated(ownGroup.getAvatar(), false); - setClippingDeactivated(ownGroup.getSender(), false); + setClippingDeactivated(ownGroup.getSenderView(), false); ownGroup.setTranslationY(0); ownGroup.getMessageContainer().setTranslationY(0); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java index 0d2209569093..adc091457364 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java @@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; -import android.os.Build; import android.view.View; import com.android.systemui.R; @@ -118,7 +117,7 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { } @Override - public boolean shouldClipToRounding() { + public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java index d7c08cc89c25..548f006c934d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java @@ -62,7 +62,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi } @Override - public boolean shouldClipToRounding() { + public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index fd085d9c2391..d463eae6e43f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -265,6 +265,15 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp updateActionOffset(); } + @Override + public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { + if (super.shouldClipToRounding(topRounded, bottomRounded)) { + return true; + } + return bottomRounded && mActionsContainer != null + && mActionsContainer.getVisibility() != View.GONE; + } + private void updateActionOffset() { if (mActionsContainer != null) { // We should never push the actions higher than they are in the headsup view. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index c71d604c9122..17eb4c110031 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -195,7 +195,7 @@ public abstract class NotificationViewWrapper implements TransformableView { return 0; } - public boolean shouldClipToRounding() { + public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { return false; } } 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 70ec45e4cdb4..42258439190e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -335,9 +335,16 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } @Override - public void onRotationProposal(final int rotation) { - // This method will only be called if rotation is valid but will include proposals for the - // current system rotation + public void onRotationProposal(final int rotation, boolean isValid) { + // This method will be called on rotation suggestion changes even if the proposed rotation + // is not valid for the top app. Use invalid rotation choices as a signal to remove the + // rotate button if shown. + + if (!isValid) { + setRotateSuggestionButtonState(false); + return; + } + Handler h = getView().getHandler(); if (rotation == mWindowManager.getDefaultDisplay().getRotation()) { // Use this as a signal to remove any current suggestions diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 4faa84aca099..ff923e56a33f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -43,6 +43,8 @@ import com.android.systemui.tuner.TunerService; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_TOP; +import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY; +import static com.android.systemui.OverviewProxyService.TAG_OPS; /** * Class to detect gestures on the navigation bar. @@ -118,6 +120,9 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture event.transform(mTransformGlobalMatrix); try { overviewProxy.onMotionEvent(event); + if (DEBUG_OVERVIEW_PROXY) { + Log.d(TAG_OPS, "Send MotionEvent: " + event.toString()); + } return true; } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index b11367523c08..e09d31cea082 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -26,6 +26,7 @@ import android.view.IWallpaperVisibilityListener; import android.view.IWindowManager; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnLayoutChangeListener; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; @@ -41,6 +42,7 @@ public final class NavigationBarTransitions extends BarTransitions { private boolean mLightsOut; private boolean mAutoDim; + private View mNavButtons; public NavigationBarTransitions(NavigationBarView view) { super(view, R.drawable.nav_background); @@ -66,6 +68,18 @@ public final class NavigationBarTransitions extends BarTransitions { }, Display.DEFAULT_DISPLAY); } catch (RemoteException e) { } + mView.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + View currentView = mView.getCurrentView(); + if (currentView != null) { + mNavButtons = currentView.findViewById(R.id.nav_buttons); + applyLightsOut(false, true); + } + }); + View currentView = mView.getCurrentView(); + if (currentView != null) { + mNavButtons = currentView.findViewById(R.id.nav_buttons); + } } public void init() { @@ -105,21 +119,20 @@ public final class NavigationBarTransitions extends BarTransitions { if (!force && lightsOut == mLightsOut) return; mLightsOut = lightsOut; - - final View navButtons = mView.getCurrentView().findViewById(R.id.nav_buttons); + if (mNavButtons == null) return; // ok, everyone, stop it right there - navButtons.animate().cancel(); + mNavButtons.animate().cancel(); // Bump percentage by 10% if dark. float darkBump = mLightTransitionsController.getCurrentDarkIntensity() / 10; final float navButtonsAlpha = lightsOut ? 0.6f + darkBump : 1f; if (!animate) { - navButtons.setAlpha(navButtonsAlpha); + mNavButtons.setAlpha(navButtonsAlpha); } else { final int duration = lightsOut ? LIGHTS_OUT_DURATION : LIGHTS_IN_DURATION; - navButtons.animate() + mNavButtons.animate() .alpha(navButtonsAlpha) .setDuration(duration) .start(); 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 f41cb293aeb0..6857337e5c10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -39,7 +39,6 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; import android.graphics.drawable.Icon; import android.media.AudioManager; import android.net.Uri; @@ -66,7 +65,6 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; -import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -146,7 +144,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private boolean mDockedStackExists; private boolean mManagedProfileIconVisible = false; - private boolean mManagedProfileInQuietMode = false; private BluetoothController mBluetooth; @@ -474,17 +471,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } } - private void updateQuietState() { - mManagedProfileInQuietMode = false; - int currentUserId = ActivityManager.getCurrentUser(); - for (UserInfo ui : mUserManager.getEnabledProfiles(currentUserId)) { - if (ui.isManagedProfile() && ui.isQuietModeEnabled()) { - mManagedProfileInQuietMode = true; - return; - } - } - } - private void updateManagedProfile() { // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in // some cases. Since it doesn't really matter here whether it's updated in this frame @@ -502,11 +488,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, mContext.getString(R.string.accessibility_managed_profile)); - } else if (mManagedProfileInQuietMode) { - showIcon = true; - mIconController.setIcon(mSlotManagedProfile, - R.drawable.stat_sys_managed_profile_status_off, - mContext.getString(R.string.accessibility_managed_profile)); } else { showIcon = false; } @@ -676,7 +657,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, public void onUserSwitchComplete(int newUserId) throws RemoteException { mHandler.post(() -> { updateAlarm(); - updateQuietState(); updateManagedProfile(); updateForegroundInstantApps(); }); @@ -724,7 +704,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, if (mCurrentUserSetup == userSetup) return; mCurrentUserSetup = userSetup; updateAlarm(); - updateQuietState(); } @Override @@ -793,7 +772,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) || action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) || action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) { - updateQuietState(); updateManagedProfile(); } else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) { updateHeadsetPlug(intent); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 970d1de251d5..e8b28f21c2c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -16,26 +16,34 @@ package com.android.systemui.statusbar.phone; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import android.annotation.Nullable; import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.EventLog; +import android.view.DisplayCutout; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import com.android.systemui.BatteryMeterView; -import com.android.systemui.DejankUtils; +import android.widget.FrameLayout; +import android.widget.LinearLayout; import com.android.systemui.Dependency; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.util.leak.RotationUtils; public class PhoneStatusBarView extends PanelBar { private static final String TAG = "PhoneStatusBarView"; private static final boolean DEBUG = StatusBar.DEBUG; private static final boolean DEBUG_GESTURES = false; + private static final int NO_VALUE = Integer.MIN_VALUE; StatusBar mBar; @@ -53,6 +61,10 @@ public class PhoneStatusBarView extends PanelBar { } }; private DarkReceiver mBattery; + private int mLastOrientation; + private View mCutoutSpace; + @Nullable + private DisplayCutout mDisplayCutout; public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -76,6 +88,7 @@ public class PhoneStatusBarView extends PanelBar { public void onFinishInflate() { mBarTransitions.init(); mBattery = findViewById(R.id.battery); + mCutoutSpace = findViewById(R.id.cutout_space_view); } @Override @@ -83,12 +96,51 @@ public class PhoneStatusBarView extends PanelBar { super.onAttachedToWindow(); // Always have Battery meters in the status bar observe the dark/light modes. Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery); + if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) { + postUpdateLayoutForCutout(); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery); + mDisplayCutout = null; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // May trigger cutout space layout-ing + if (updateOrientationAndCutout(newConfig.orientation)) { + postUpdateLayoutForCutout(); + } + } + + /** + * + * @param newOrientation may pass NO_VALUE for no change + * @return boolean indicating if we need to update the cutout location / margins + */ + private boolean updateOrientationAndCutout(int newOrientation) { + boolean changed = false; + if (newOrientation != NO_VALUE) { + if (mLastOrientation != newOrientation) { + changed = true; + mLastOrientation = newOrientation; + } + } + + if (mDisplayCutout == null) { + DisplayCutout cutout = getRootWindowInsets().getDisplayCutout(); + if (cutout != null) { + changed = true; + mDisplayCutout = cutout; + } + } + + return changed; } @Override @@ -214,4 +266,75 @@ public class PhoneStatusBarView extends PanelBar { R.dimen.status_bar_height); setLayoutParams(layoutParams); } + + private void updateLayoutForCutout() { + updateCutoutLocation(); + updateSafeInsets(); + } + + private void postUpdateLayoutForCutout() { + Runnable r = new Runnable() { + @Override + public void run() { + updateLayoutForCutout(); + } + }; + // Let the cutout emulation draw first + postDelayed(r, 0); + } + + private void updateCutoutLocation() { + if (mDisplayCutout == null || mDisplayCutout.isEmpty() + || mLastOrientation != ORIENTATION_PORTRAIT) { + mCutoutSpace.setVisibility(View.GONE); + return; + } + + mCutoutSpace.setVisibility(View.VISIBLE); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams(); + lp.width = mDisplayCutout.getBoundingRect().width(); + lp.height = mDisplayCutout.getBoundingRect().height(); + } + + private void updateSafeInsets() { + // Depending on our rotation, we may have to work around a cutout in the middle of the view, + // or letterboxing from the right or left sides. + + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + if (mDisplayCutout == null || mDisplayCutout.isEmpty()) { + lp.leftMargin = 0; + lp.rightMargin = 0; + return; + } + + int leftMargin = 0; + int rightMargin = 0; + switch (RotationUtils.getRotation(getContext())) { + /* + * Landscape: <-| + * Seascape: |-> + */ + case RotationUtils.ROTATION_LANDSCAPE: + leftMargin = getDisplayCutoutHeight(); + break; + case RotationUtils.ROTATION_SEASCAPE: + rightMargin = getDisplayCutoutHeight(); + break; + default: + break; + } + + lp.leftMargin = leftMargin; + lp.rightMargin = rightMargin; + } + + //TODO: Find a better way + private int getDisplayCutoutHeight() { + if (mDisplayCutout == null || mDisplayCutout.isEmpty()) { + return 0; + } + + Rect r = mDisplayCutout.getBoundingRect(); + return r.bottom - r.top; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java index 9f8a7efa04f3..6fc5bbdd5977 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java @@ -49,6 +49,8 @@ import com.android.systemui.shared.recents.utilities.Utilities; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; +import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY; +import static com.android.systemui.OverviewProxyService.TAG_OPS; /** * Class to detect gestures on the navigation bar and implement quick scrub and switch. @@ -144,6 +146,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene try { final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy(); overviewProxy.onQuickSwitch(); + if (DEBUG_OVERVIEW_PROXY) { + Log.d(TAG_OPS, "Quick Switch"); + } } catch (RemoteException e) { Log.e(TAG, "Failed to send start of quick switch.", e); } @@ -207,21 +212,31 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene int y = (int) event.getY(); int xDiff = Math.abs(x - mTouchDownX); int yDiff = Math.abs(y - mTouchDownY); - boolean exceededTouchSlop; + boolean exceededTouchSlopX = xDiff > mScrollTouchSlop && xDiff > yDiff; + boolean exceededTouchSlopY = yDiff > mScrollTouchSlop && yDiff > xDiff; + boolean exceededTouchSlop, exceededPerpendicularTouchSlop; int pos, touchDown, offset, trackSize; + if (mIsVertical) { - exceededTouchSlop = yDiff > mScrollTouchSlop && yDiff > xDiff; + exceededTouchSlop = exceededTouchSlopY; + exceededPerpendicularTouchSlop = exceededTouchSlopX; pos = y; touchDown = mTouchDownY; offset = pos - mTrackRect.top; trackSize = mTrackRect.height(); } else { - exceededTouchSlop = xDiff > mScrollTouchSlop && xDiff > yDiff; + exceededTouchSlop = exceededTouchSlopX; + exceededPerpendicularTouchSlop = exceededTouchSlopY; pos = x; touchDown = mTouchDownX; offset = pos - mTrackRect.left; trackSize = mTrackRect.width(); } + // Do not start scrubbing when dragging in the perpendicular direction + if (!mDraggingActive && exceededPerpendicularTouchSlop) { + mHandler.removeCallbacksAndMessages(null); + return false; + } if (!mDragPositive) { offset -= mIsVertical ? mTrackRect.height() : mTrackRect.width(); } @@ -246,6 +261,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene if (mQuickScrubActive) { try { overviewProxy.onQuickScrubProgress(scrubFraction); + if (DEBUG_OVERVIEW_PROXY) { + Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction); + } } catch (RemoteException e) { Log.e(TAG, "Failed to send progress of quick scrub.", e); } @@ -294,12 +312,14 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene // Get the touch rect of the home button location View homeView = mNavigationBarView.getHomeButton().getCurrentView(); - int[] globalHomePos = homeView.getLocationOnScreen(); - int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen(); - int homeX = globalHomePos[0] - globalNavBarPos[0]; - int homeY = globalHomePos[1] - globalNavBarPos[1]; - mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(), - homeY + homeView.getMeasuredHeight()); + if (homeView != null) { + int[] globalHomePos = homeView.getLocationOnScreen(); + int[] globalNavBarPos = mNavigationBarView.getLocationOnScreen(); + int homeX = globalHomePos[0] - globalNavBarPos[0]; + int homeY = globalHomePos[1] - globalNavBarPos[1]; + mHomeButtonRect.set(homeX, homeY, homeX + homeView.getMeasuredWidth(), + homeY + homeView.getMeasuredHeight()); + } } @Override @@ -343,6 +363,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene mTrackAnimator.start(); try { mOverviewEventSender.getProxy().onQuickScrubStart(); + if (DEBUG_OVERVIEW_PROXY) { + Log.d(TAG_OPS, "Quick Scrub Start"); + } } catch (RemoteException e) { Log.e(TAG, "Failed to send start of quick scrub.", e); } @@ -357,6 +380,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene mQuickScrubEndAnimator.start(); try { mOverviewEventSender.getProxy().onQuickScrubEnd(); + if (DEBUG_OVERVIEW_PROXY) { + Log.d(TAG_OPS, "Quick Scrub End"); + } } catch (RemoteException e) { Log.e(TAG, "Failed to send end of quick scrub.", e); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 14329b564800..3b394ddd63cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -866,13 +866,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" ScrimController:"); - pw.print(" state:"); pw.println(mState); - pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha()); + pw.println(" ScrimController: "); + pw.print(" state: "); pw.println(mState); + pw.print(" frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha()); pw.print(" alpha="); pw.print(mCurrentInFrontAlpha); pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint())); - pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha()); + pw.print(" backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha()); pw.print(" alpha="); pw.print(mCurrentBehindAlpha); pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint())); 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 af65a86676e8..c30fb22630da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1704,7 +1704,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mReportRejectedTouch == null) { return; } - mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD + mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } @@ -2663,6 +2663,10 @@ public class StatusBar extends SystemUI implements DemoMode, mFingerprintUnlockController.dump(pw); } + if (mKeyguardIndicationController != null) { + mKeyguardIndicationController.dump(fd, pw, args); + } + if (mScrimController != null) { mScrimController.dump(fd, pw, args); } @@ -4506,6 +4510,7 @@ public class StatusBar extends SystemUI implements DemoMode, ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing); } updateDozingState(); + updateReportRejectedTouchVisibility(); Trace.endSection(); } @@ -4931,18 +4936,11 @@ public class StatusBar extends SystemUI implements DemoMode, // system process is dead if we're here. } if (parentToCancelFinal != null) { - // We have to post it to the UI thread for synchronization - mHandler.post(() -> { - Runnable removeRunnable = - () -> mEntryManager.performRemoveNotification(parentToCancelFinal); - if (isCollapsing()) { - // To avoid lags we're only performing the remove - // after the shade was collapsed - addPostCollapseAction(removeRunnable); - } else { - removeRunnable.run(); - } - }); + removeNotification(parentToCancelFinal); + } + if (shouldAutoCancel(sbn)) { + // Automatically remove all notifications that we may have kept around longer + removeNotification(sbn); } }; @@ -4966,6 +4964,21 @@ public class StatusBar extends SystemUI implements DemoMode, }, afterKeyguardGone); } + private void removeNotification(StatusBarNotification notification) { + // We have to post it to the UI thread for synchronization + mHandler.post(() -> { + Runnable removeRunnable = + () -> mEntryManager.performRemoveNotification(notification); + if (isCollapsing()) { + // To avoid lags we're only performing the remove + // after the shade was collapsed + addPostCollapseAction(removeRunnable); + } else { + removeRunnable.run(); + } + }); + } + protected NotificationListener mNotificationListener; protected void notifyUserAboutHiddenNotifications() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index bcda60ebc62c..07610ceff7b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -62,7 +62,7 @@ public interface StatusBarIconController { } /** - * Version of ViewGroup that observers state from the DarkIconDispatcher. + * Version of ViewGroup that observes state from the DarkIconDispatcher. */ public static class DarkIconManager extends IconManager { private final DarkIconDispatcher mDarkIconDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java new file mode 100644 index 000000000000..1897171b5e54 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -0,0 +1,169 @@ +/* + * 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. + */ + +/** + * A container for Status bar system icons. Limits the number of system icons and handles overflow + * similar to NotificationIconController. Can be used to layout nested StatusIconContainers + * + * Children are expected to be of type StatusBarIconView. + */ +package com.android.systemui.statusbar.phone; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.ArrayMap; +import android.util.AttributeSet; + +import android.view.View; +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.stack.ViewState; + +public class StatusIconContainer extends AlphaOptimizedLinearLayout { + + private static final String TAG = "StatusIconContainer"; + private static final int MAX_ICONS = 5; + private static final int MAX_DOTS = 3; + + public StatusIconContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + float midY = getHeight() / 2.0f; + + // Layout all child views so that we can move them around later + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + int width = child.getMeasuredWidth(); + int height = child.getMeasuredHeight(); + int top = (int) (midY - height / 2.0f); + child.layout(0, top, width, top + height); + } + + resetViewStates(); + calculateIconTranslations(); + applyIconStates(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + final int count = getChildCount(); + // Measure all children so that they report the correct width + for (int i = 0; i < count; i++) { + measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + ViewState vs = new ViewState(); + child.setTag(R.id.status_bar_view_state_tag, vs); + } + + @Override + public void onViewRemoved(View child) { + super.onViewRemoved(child); + child.setTag(R.id.status_bar_view_state_tag, null); + } + + /** + * Layout is happening from end -> start + */ + private void calculateIconTranslations() { + float translationX = getWidth(); + float contentStart = getPaddingStart(); + int childCount = getChildCount(); + // Underflow === don't show content until that index + int firstUnderflowIndex = -1; + android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX); + + //TODO: Dots + for (int i = childCount - 1; i >= 0; i--) { + View child = getChildAt(i); + if (!(child instanceof StatusBarIconView)) { + continue; + } + + ViewState childState = getViewStateFromChild(child); + if (childState == null ) { + continue; + } + + // Rely on StatusBarIcon for truth about visibility + if (!((StatusBarIconView) child).getStatusBarIcon().visible) { + childState.hidden = true; + continue; + } + + childState.xTranslation = translationX - child.getWidth(); + + if (childState.xTranslation < contentStart) { + if (firstUnderflowIndex == -1) { + firstUnderflowIndex = i; + } + } + + translationX -= child.getWidth(); + } + + if (firstUnderflowIndex != -1) { + for (int i = 0; i <= firstUnderflowIndex; i++) { + View child = getChildAt(i); + ViewState vs = getViewStateFromChild(child); + if (vs != null) { + vs.hidden = true; + } + } + } + } + + private void applyIconStates() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + ViewState vs = getViewStateFromChild(child); + if (vs != null) { + vs.applyToView(child); + } + } + } + + private void resetViewStates() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + ViewState vs = getViewStateFromChild(child); + if (vs == null) { + continue; + } + + vs.initFrom(child); + vs.alpha = 1.0f; + if (child instanceof StatusBarIconView) { + vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible; + } else { + vs.hidden = false; + } + } + } + + private static @Nullable ViewState getViewStateFromChild(View child) { + return (ViewState) child.getTag(R.id.status_bar_view_state_tag); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java index 2951943404b3..2ede327ab698 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverControllerImpl.java @@ -74,17 +74,9 @@ public class DataSaverControllerImpl implements DataSaverController { } } - private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { + private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() { @Override - public void onUidRulesChanged(int uid, int uidRules) throws RemoteException { - } - - @Override - public void onMeteredIfacesChanged(String[] strings) throws RemoteException { - } - - @Override - public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException { + public void onRestrictBackgroundChanged(final boolean isDataSaving) { mHandler.post(new Runnable() { @Override public void run() { @@ -92,10 +84,6 @@ public class DataSaverControllerImpl implements DataSaverController { } }); } - - @Override - public void onUidPoliciesChanged(int uid, int uidPolicies) throws RemoteException { - } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 4fc504420f1f..c377feb0b2e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -27,7 +27,9 @@ import android.content.pm.ShortcutManager; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.SystemClock; import android.text.Editable; +import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; @@ -140,6 +142,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); mEntry.remoteInputText = mEditText.getText(); + mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); mController.addSpinning(mEntry.key, mToken); mController.removeRemoteInput(mEntry, mToken); mEditText.mShowImeOnInputConnection = false; @@ -298,6 +301,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private void reset() { mResetting = true; + mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText()); mEditText.getText().clear(); mEditText.setEnabled(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 4d8da441c039..ebf4cda457e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.stack; +import android.annotation.Nullable; import android.content.Context; import android.view.View; @@ -236,6 +237,7 @@ public class AmbientState { mShelf = shelf; } + @Nullable public NotificationShelf getShelf() { return mShelf; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index 55050995d5f6..4ca33cd3f601 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -394,7 +394,7 @@ public class NotificationChildrenContainer extends ViewGroup { } } else if (mOverflowNumber != null) { removeView(mOverflowNumber); - if (isShown()) { + if (isShown() && isAttachedToWindow()) { final View removedOverflowNumber = mOverflowNumber; addTransientView(removedOverflowNumber, getTransientViewCount()); CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 7374f115a19b..2ce6df275588 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -122,7 +122,9 @@ public class StackScrollAlgorithm { } private void updateShelfState(StackScrollState resultState, AmbientState ambientState) { NotificationShelf shelf = ambientState.getShelf(); - shelf.updateState(resultState, ambientState); + if (shelf != null) { + shelf.updateState(resultState, ambientState); + } } private void updateClipping(StackScrollState resultState, @@ -495,6 +497,10 @@ public class StackScrollAlgorithm { */ private void clampPositionToShelf(ExpandableViewState childViewState, AmbientState ambientState) { + if (ambientState.getShelf() == null) { + return; + } + int shelfStart = ambientState.getInnerHeight() - ambientState.getShelf().getIntrinsicHeight(); childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart); @@ -556,7 +562,8 @@ public class StackScrollAlgorithm { } else if (i == 0 && ambientState.isAboveShelf(child)) { // In case this is a new view that has never been measured before, we don't want to // elevate if we are currently expanded more then the notification - int shelfHeight = ambientState.getShelf().getIntrinsicHeight(); + int shelfHeight = ambientState.getShelf() == null ? 0 : + ambientState.getShelf().getIntrinsicHeight(); float shelfStart = ambientState.getInnerHeight() - shelfHeight + ambientState.getTopPadding() + ambientState.getStackTranslation(); diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java new file mode 100644 index 000000000000..3423452c2a27 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java @@ -0,0 +1,51 @@ +/* + * 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 com.android.systemui.volume; + +import android.support.v7.media.MediaRouteSelector; +import android.support.v7.media.MediaRouter; + +import java.util.List; + +/** + * Wrapper for final class MediaRouter, for testing. + */ +public class MediaRouterWrapper { + + private final MediaRouter mRouter; + + public MediaRouterWrapper(MediaRouter router) + { + mRouter = router; + } + + public void addCallback(MediaRouteSelector selector, MediaRouter.Callback callback, int flags) { + mRouter.addCallback(selector, callback, flags); + } + + public void removeCallback(MediaRouter.Callback callback) { + mRouter.removeCallback(callback); + } + + public void unselect(int reason) { + mRouter.unselect(reason); + } + + public List<MediaRouter.RouteInfo> getRoutes() { + return mRouter.getRoutes(); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java index f8843a997d59..e0af9baf0bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -40,6 +40,7 @@ import android.os.SystemClock; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; +import android.telecom.TelecomManager; import android.util.Log; import android.util.Pair; @@ -54,7 +55,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -65,15 +65,17 @@ public class OutputChooserDialog extends SystemUIDialog private static final int MAX_DEVICES = 10; private static final long UPDATE_DELAY_MS = 300L; - static final int MSG_UPDATE_ITEMS = 1; + private static final int MSG_UPDATE_ITEMS = 1; private final Context mContext; - private final BluetoothController mController; - private final WifiManager mWifiManager; + private final BluetoothController mBluetoothController; + private WifiManager mWifiManager; private OutputChooserLayout mView; - private final MediaRouter mRouter; + private final MediaRouterWrapper mRouter; private final MediaRouterCallback mRouterCallback; private long mLastUpdateTime; + private boolean mIsInCall; + protected boolean isAttached; private final MediaRouteSelector mRouteSelector; private Drawable mDefaultIcon; @@ -81,12 +83,14 @@ public class OutputChooserDialog extends SystemUIDialog private Drawable mSpeakerIcon; private Drawable mSpeakerGroupIcon; - public OutputChooserDialog(Context context) { + public OutputChooserDialog(Context context, MediaRouterWrapper router) { super(context); mContext = context; - mController = Dependency.get(BluetoothController.class); + mBluetoothController = Dependency.get(BluetoothController.class); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mRouter = MediaRouter.getInstance(context); + TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + mIsInCall = tm.isInCall(); + mRouter = router; mRouterCallback = new MediaRouterCallback(); mRouteSelector = new MediaRouteSelector.Builder() .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) @@ -96,27 +100,38 @@ public class OutputChooserDialog extends SystemUIDialog context.registerReceiver(mReceiver, filter); } + protected void setIsInCall(boolean inCall) { + mIsInCall = inCall; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.output_chooser); setCanceledOnTouchOutside(true); setOnDismissListener(this::onDismiss); - setTitle(R.string.output_title); mView = findViewById(R.id.output_chooser); mView.setCallback(this); + if (mIsInCall) { + mView.setTitle(R.string.output_calls_title); + } else { + mView.setTitle(R.string.output_title); + } + mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast); mTvIcon = mContext.getDrawable(R.drawable.ic_tv); mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker); mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group); final boolean wifiOff = !mWifiManager.isWifiEnabled(); - final boolean btOff = !mController.isBluetoothEnabled(); - if (wifiOff || btOff) { + final boolean btOff = !mBluetoothController.isBluetoothEnabled(); + if (wifiOff && btOff) { mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff)); } + // time out after 5 seconds + mView.postDelayed(() -> updateItems(true), 5000); } protected void cleanUp() {} @@ -131,15 +146,19 @@ public class OutputChooserDialog extends SystemUIDialog public void onAttachedToWindow() { super.onAttachedToWindow(); - mRouter.addCallback(mRouteSelector, mRouterCallback, - MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - mController.addCallback(mCallback); + if (!mIsInCall) { + mRouter.addCallback(mRouteSelector, mRouterCallback, + MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + } + mBluetoothController.addCallback(mCallback); + isAttached = true; } @Override public void onDetachedFromWindow() { + isAttached = false; mRouter.removeCallback(mRouterCallback); - mController.removeCallback(mCallback); + mBluetoothController.removeCallback(mCallback); super.onDetachedFromWindow(); } @@ -154,9 +173,8 @@ public class OutputChooserDialog extends SystemUIDialog if (item == null || item.tag == null) return; if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) { final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag; - if (device != null && device.getMaxConnectionState() - == BluetoothProfile.STATE_DISCONNECTED) { - mController.connect(device); + if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) { + mBluetoothController.connect(device); } } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) { final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag; @@ -171,18 +189,16 @@ public class OutputChooserDialog extends SystemUIDialog if (item == null || item.tag == null) return; if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) { final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag; - if (device != null) { - mController.disconnect(device); - } + mBluetoothController.disconnect(device); } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) { mRouter.unselect(UNSELECT_REASON_DISCONNECTED); } } - private void updateItems() { + private void updateItems(boolean timeout) { if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) { mHandler.removeMessages(MSG_UPDATE_ITEMS); - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS), + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout), mLastUpdateTime + UPDATE_DELAY_MS); return; } @@ -194,14 +210,16 @@ public class OutputChooserDialog extends SystemUIDialog addBluetoothDevices(items); // Add remote displays - addRemoteDisplayRoutes(items); + if (!mIsInCall) { + addRemoteDisplayRoutes(items); + } - Collections.sort(items, ItemComparator.sInstance); + items.sort(ItemComparator.sInstance); - if (items.size() == 0) { + if (items.size() == 0 && timeout) { String emptyMessage = mContext.getString(R.string.output_none_found); final boolean wifiOff = !mWifiManager.isWifiEnabled(); - final boolean btOff = !mController.isBluetoothEnabled(); + final boolean btOff = !mBluetoothController.isBluetoothEnabled(); if (wifiOff || btOff) { emptyMessage = getDisabledServicesMessage(wifiOff, btOff); } @@ -219,12 +237,12 @@ public class OutputChooserDialog extends SystemUIDialog } private void addBluetoothDevices(List<OutputChooserLayout.Item> items) { - final Collection<CachedBluetoothDevice> devices = mController.getDevices(); + final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices(); if (devices != null) { int connectedDevices = 0; int count = 0; for (CachedBluetoothDevice device : devices) { - if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; + if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; final int majorClass = device.getBtClass().getMajorDeviceClass(); if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) { @@ -328,22 +346,22 @@ public class OutputChooserDialog extends SystemUIDialog private final class MediaRouterCallback extends MediaRouter.Callback { @Override public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { - dismiss(); + updateItems(false); } } @@ -361,12 +379,12 @@ public class OutputChooserDialog extends SystemUIDialog private final BluetoothController.Callback mCallback = new BluetoothController.Callback() { @Override public void onBluetoothStateChange(boolean enabled) { - updateItems(); + updateItems(false); } @Override public void onBluetoothDevicesChanged() { - updateItems(); + updateItems(false); } }; @@ -393,7 +411,7 @@ public class OutputChooserDialog extends SystemUIDialog public void handleMessage(Message message) { switch (message.what) { case MSG_UPDATE_ITEMS: - updateItems(); + updateItems((Boolean) message.obj); break; } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java index 22ced60006b0..d4c6f897846e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java @@ -29,8 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.systemui.FontSizeUtils; @@ -40,11 +40,10 @@ import com.android.systemui.qs.AutoSizingList; /** * Limited height list of devices. */ -public class OutputChooserLayout extends FrameLayout { +public class OutputChooserLayout extends LinearLayout { private static final String TAG = "OutputChooserLayout"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final int mQsDetailIconOverlaySize; private final Context mContext; private final H mHandler = new H(); private final Adapter mAdapter = new Adapter(); @@ -55,6 +54,7 @@ public class OutputChooserLayout extends FrameLayout { private AutoSizingList mItemList; private View mEmpty; private TextView mEmptyText; + private TextView mTitle; private Item[] mItems; @@ -62,8 +62,6 @@ public class OutputChooserLayout extends FrameLayout { super(context, attrs); mContext = context; mTag = TAG; - mQsDetailIconOverlaySize = (int) getResources().getDimension( - R.dimen.qs_detail_icon_overlay_size); } @Override @@ -74,7 +72,8 @@ public class OutputChooserLayout extends FrameLayout { mItemList.setAdapter(mAdapter); mEmpty = findViewById(android.R.id.empty); mEmpty.setVisibility(GONE); - mEmptyText = mEmpty.findViewById(android.R.id.title); + mEmptyText = mEmpty.findViewById(R.id.empty_text); + mTitle = findViewById(R.id.title); } @Override @@ -84,17 +83,21 @@ public class OutputChooserLayout extends FrameLayout { int count = mItemList.getChildCount(); for (int i = 0; i < count; i++) { View item = mItemList.getChildAt(i); - FontSizeUtils.updateFontSize(item, android.R.id.title, + FontSizeUtils.updateFontSize(item, R.id.empty_text, R.dimen.qs_detail_item_primary_text_size); FontSizeUtils.updateFontSize(item, android.R.id.summary, R.dimen.qs_detail_item_secondary_text_size); + FontSizeUtils.updateFontSize(item, android.R.id.title, + R.dimen.qs_detail_header_text_size); } } + public void setTitle(int title) { + mTitle.setText(title); + } + public void setEmptyState(String text) { - mEmpty.post(() -> { - mEmptyText.setText(text); - }); + mEmptyText.setText(text); } @Override @@ -176,11 +179,6 @@ public class OutputChooserLayout extends FrameLayout { } else { iv.setImageResource(item.iconResId); } - iv.getOverlay().clear(); - if (item.overlay != null) { - item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize); - iv.getOverlay().add(item.overlay); - } final TextView title = view.findViewById(android.R.id.title); title.setText(item.line1); final TextView summary = view.findViewById(android.R.id.summary); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 5a19a7677649..e76bf572b1a1 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -47,6 +47,7 @@ import android.os.Message; import android.os.SystemClock; import android.provider.Settings; import android.provider.Settings.Global; +import android.support.v7.media.MediaRouter; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -103,7 +104,6 @@ public class VolumeDialogImpl implements VolumeDialog { private ViewGroup mDialogView; private ViewGroup mDialogRowsView; private ImageButton mRingerIcon; - private ImageButton mOutputChooser; private TextView mRingerStatus; private final List<VolumeRow> mRows = new ArrayList<>(); private ConfigurableTexts mConfigurableTexts; @@ -225,9 +225,6 @@ public class VolumeDialogImpl implements VolumeDialog { addExistingRows(); } - mOutputChooser = mDialogView.findViewById(R.id.output_chooser); - mOutputChooser.setOnClickListener(mClickOutputChooser); - updateRowsH(getActiveRow()); initRingerH(); } @@ -335,6 +332,9 @@ public class VolumeDialogImpl implements VolumeDialog { row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row)); row.anim = null; + ImageButton outputChooser = row.view.findViewById(R.id.output_chooser); + outputChooser.setOnClickListener(mClickOutputChooser); + // forward events above the slider into the slider row.view.setOnTouchListener(new OnTouchListener() { private final Rect mSliderHitRect = new Rect(); @@ -368,16 +368,16 @@ public class VolumeDialogImpl implements VolumeDialog { mController.setActiveStream(row.stream); if (row.stream == AudioManager.STREAM_RING) { final boolean hasVibrator = mController.hasVibrator(); - if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { + if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) { if (hasVibrator) { - mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); + mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true); } else { final boolean wasZero = row.ss.level == 0; mController.setStreamVolume(stream, wasZero ? row.lastAudibleLevel : 0); } } else { - mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); + mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true); if (row.ss.level == 0) { mController.setStreamVolume(stream, 1); } @@ -403,15 +403,15 @@ public class VolumeDialogImpl implements VolumeDialog { return; } final boolean hasVibrator = mController.hasVibrator(); - if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { + if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) { if (hasVibrator) { - mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); + mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true); } else { final boolean wasZero = ss.level == 0; mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0); } } else { - mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); + mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true); if (ss.level == 0) { mController.setStreamVolume(AudioManager.STREAM_RING, 1); } @@ -552,7 +552,7 @@ public class VolumeDialogImpl implements VolumeDialog { if (ss == null) { return; } - switch (mState.ringerModeInternal) { + switch (mState.ringerModeExternal) { case AudioManager.RINGER_MODE_VIBRATE: mRingerStatus.setText(R.string.volume_ringer_status_vibrate); mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); @@ -653,9 +653,9 @@ public class VolumeDialogImpl implements VolumeDialog { final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM; final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC; final boolean isRingVibrate = isRingStream - && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; + && mState.ringerModeExternal == AudioManager.RINGER_MODE_VIBRATE; final boolean isRingSilent = isRingStream - && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT; + && mState.ringerModeExternal == AudioManager.RINGER_MODE_SILENT; final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS; final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream) @@ -859,7 +859,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (mOutputChooserDialog != null) { return; } - mOutputChooserDialog = new OutputChooserDialog(mContext) { + mOutputChooserDialog = new OutputChooserDialog(mContext, + new MediaRouterWrapper(MediaRouter.getInstance(mContext))) { @Override protected void cleanUp() { synchronized (mOutputChooserLock) { diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index e74736ab2fcc..859dc2f18dc6 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -46,6 +46,7 @@ <uses-permission android:name="android.permission.STATUS_BAR" /> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index fbcbd2096db7..4c7c1d1608fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -83,7 +83,7 @@ public abstract class SysuiTestCase { return null; } - public Context getContext() { + public SysuiTestableContext getContext() { return mContext; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 960320722c56..a02ef98f834d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -119,9 +119,9 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testShowRecentApps() { - mCommandQueue.showRecentApps(true, false); + mCommandQueue.showRecentApps(true); waitForIdleSync(); - verify(mCallbacks).showRecentApps(eq(true), eq(false)); + verify(mCallbacks).showRecentApps(eq(true)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java new file mode 100644 index 000000000000..8f38c2ca948d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java @@ -0,0 +1,196 @@ +/* + * 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 com.android.systemui.statusbar; + +import static junit.framework.Assert.assertNotNull; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.NotificationGuts; +import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.NotificationInflater; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoRule; +import org.mockito.junit.MockitoJUnit; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationGutsManagerTest extends SysuiTestCase { + private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; + + private final String mPackageName = mContext.getPackageName(); + private final int mUid = Binder.getCallingUid(); + + private NotificationChannel mTestNotificationChannel = new NotificationChannel( + TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); + private TestableLooper mTestableLooper; + private Handler mHandler; + private NotificationTestHelper mHelper; + private NotificationGutsManager mGutsManager; + + @Rule public MockitoRule mockito = MockitoJUnit.rule(); + @Mock private NotificationLockscreenUserManager mLockscreenUserManager; + @Mock private NotificationPresenter mPresenter; + @Mock private NotificationEntryManager mEntryManager; + @Mock private NotificationStackScrollLayout mStackScroller; + @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener; + @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener; + + @Before + public void setUp() { + mTestableLooper = TestableLooper.get(this); + mHandler = new Handler(mTestableLooper.getLooper()); + + mHelper = new NotificationTestHelper(mContext); + + mGutsManager = new NotificationGutsManager(mContext); + mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller, + mCheckSaveListener, mOnSettingsClickListener); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Test methods: + + @Test + public void testOpenAndCloseGuts() { + NotificationGuts guts = spy(new NotificationGuts(mContext)); + when(guts.post(any())).thenAnswer(invocation -> { + mHandler.post(((Runnable) invocation.getArguments()[0])); + return null; + }); + + // Test doesn't support animation since the guts view is not attached. + doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class)); + + ExpandableNotificationRow realRow = createTestNotificationRow(); + NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow); + + ExpandableNotificationRow row = spy(realRow); + when(row.getWindowToken()).thenReturn(new Binder()); + when(row.getGuts()).thenReturn(guts); + + mGutsManager.openGuts(row, 0, 0, menuItem); + assertEquals(View.INVISIBLE, guts.getVisibility()); + mTestableLooper.processAllMessages(); + verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class)); + + assertEquals(View.VISIBLE, guts.getVisibility()); + mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false); + + verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean()); + verify(row, times(1)).setGutsView(any()); + } + + @Test + public void testChangeDensityOrFontScale() { + NotificationGuts guts = spy(new NotificationGuts(mContext)); + when(guts.post(any())).thenAnswer(invocation -> { + mHandler.post(((Runnable) invocation.getArguments()[0])); + return null; + }); + + // Test doesn't support animation since the guts view is not attached. + doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class)); + + ExpandableNotificationRow realRow = createTestNotificationRow(); + NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow); + + ExpandableNotificationRow row = spy(realRow); + when(row.getWindowToken()).thenReturn(new Binder()); + when(row.getGuts()).thenReturn(guts); + doNothing().when(row).inflateGuts(); + + mGutsManager.openGuts(row, 0, 0, menuItem); + mTestableLooper.processAllMessages(); + verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class)); + + row.onDensityOrFontScaleChanged(); + mGutsManager.onDensityOrFontScaleChanged(row); + mTestableLooper.processAllMessages(); + + mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false); + + verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean()); + verify(row, times(2)).setGutsView(any()); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Utility methods: + + private ExpandableNotificationRow createTestNotificationRow() { + Notification.Builder nb = new Notification.Builder(mContext, + mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setColorized(true) + .setFlag(Notification.FLAG_CAN_COLORIZE, true) + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + try { + ExpandableNotificationRow row = mHelper.createRow(nb.build()); + row.getEntry().channel = mTestNotificationChannel; + return row; + } catch (Exception e) { + fail(); + return null; + } + } + + private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) { + NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext); + menuRow.createMenu(row, row.getStatusBarNotification()); + + NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext); + assertNotNull(menuItem); + return menuItem; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index 66524cc2b193..2edcd01ed445 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -74,6 +74,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) .getVisibility()); + assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock) + .getVisibility()); } @Test @@ -87,11 +89,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) .getVisibility()); + assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock) + .getVisibility()); fragment.disable(0, 0, false); assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area) .getVisibility()); + assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock) + .getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java new file mode 100644 index 000000000000..537d606365c4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -0,0 +1,158 @@ +/* + * 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 com.android.systemui.volume; + +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothProfile; +import android.net.wifi.WifiManager; +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.support.v7.media.MediaRouter; +import android.telecom.TelecomManager; +import android.widget.TextView; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.BluetoothController; + +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(AndroidJUnit4.class) +public class OutputChooserDialogTest extends SysuiTestCase { + + OutputChooserDialog mDialog; + + @Mock + private BluetoothController mController; + @Mock + private WifiManager mWifiManager; + @Mock + private TelecomManager mTelecomManager; + + @Mock + private MediaRouterWrapper mRouter; + + + @Before + @UiThreadTest + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + mController = mDependency.injectMockDependency(BluetoothController.class); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + + getContext().addMockSystemService(WifiManager.class, mWifiManager); + getContext().addMockSystemService(TelecomManager.class, mTelecomManager); + + mDialog = new OutputChooserDialog(getContext(), mRouter); + } + + @After + @UiThreadTest + public void tearDown() throws Exception { + mDialog.dismiss(); + } + + private void showDialog() { + mDialog.show(); + } + + @Test + @UiThreadTest + public void testClickMediaRouterItemConnectsMedia() { + showDialog(); + + OutputChooserLayout.Item item = new OutputChooserLayout.Item(); + item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER; + MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class); + when(info.isEnabled()).thenReturn(true); + item.tag = info; + + mDialog.onDetailItemClick(item); + verify(info, times(1)).select(); + verify(mController, never()).connect(any()); + } + + @Test + @UiThreadTest + public void testClickBtItemConnectsBt() { + showDialog(); + + OutputChooserLayout.Item item = new OutputChooserLayout.Item(); + item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT; + CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class); + when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED); + item.tag = btDevice; + + mDialog.onDetailItemClick(item); + verify(mController, times(1)).connect(any()); + } + + @Test + @UiThreadTest + public void testTitleNotInCall() { + showDialog(); + + assertTrue(((TextView) mDialog.findViewById(R.id.title)) + .getText().toString().contains("Media")); + } + + @Test + @UiThreadTest + public void testTitleInCall() { + mDialog.setIsInCall(true); + showDialog(); + + assertTrue(((TextView) mDialog.findViewById(R.id.title)) + .getText().toString().contains("Phone")); + } + + @Test + @UiThreadTest + public void testNoMediaScanIfInCall() { + mDialog.setIsInCall(true); + mDialog.onAttachedToWindow(); + + verify(mRouter, never()).addCallback(any(), any(), anyInt()); + } + + @Test + @UiThreadTest + public void testMediaScanIfNotInCall() { + mDialog.setIsInCall(false); + mDialog.onAttachedToWindow(); + + verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); + } +} diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml index 7091ce7ee941..526541d26b08 100644 --- a/packages/VpnDialogs/res/values-hi/strings.xml +++ b/packages/VpnDialogs/res/values-hi/strings.xml @@ -17,7 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string> - <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN कनेक्शन सेट करना चाहता है जो उसे नेटवर्क ट्रैफ़िक मॉनीटर करने देता है. केवल तभी स्वीकार करें, जबकि आप स्रोत पर भरोसा करते हों. VPN सक्रिय होने पर आपकी स्क्रीन पर ऊपर <br /> <br /> <img src=vpn_icon /> दिखाई देता है."</string> + <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN कनेक्शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको आप इस पर भरोसा हो. VPN चालू होने पर आपकी स्क्रीन के सबसे ऊपर <br /> <br /> <img src=vpn_icon /> दिखाई देता है."</string> <string name="legacy_title" msgid="192936250066580964">"VPN कनेक्ट है"</string> <string name="session" msgid="6470628549473641030">"सत्र:"</string> <string name="duration" msgid="3584782459928719435">"अवधि:"</string> diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml index b45d3b90f9d5..be572d4408f8 100644 --- a/packages/VpnDialogs/res/values-nb/strings.xml +++ b/packages/VpnDialogs/res/values-nb/strings.xml @@ -32,5 +32,5 @@ <string name="configure" msgid="4905518375574791375">"Konfigurer"</string> <string name="disconnect" msgid="971412338304200056">"Koble fra"</string> <string name="open_app" msgid="3717639178595958667">"Åpne appen"</string> - <string name="dismiss" msgid="6192859333764711227">"Avvis"</string> + <string name="dismiss" msgid="6192859333764711227">"Lukk"</string> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml index 88c19c7ccf05..30e8b689883d 100644 --- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml @@ -18,17 +18,26 @@ <!-- The bounding path of the cutout region of the main built-in display. Must either be empty if there is no cutout region, or a string that is parsable by - {@link android.util.PathParser}. --> + {@link android.util.PathParser}. + + The path is assumed to be specified in display coordinates with pixel units and in + the display's native orientation, with the origin of the coordinate system at the + center top of the display. + + To facilitate writing device-independent emulation overlays, the marker `@dp` can be + appended after the path string to interpret coordinates in dp instead of px units. + Note that a physical cutout should be configured in pixels for the best results. + --> <string translatable="false" name="config_mainBuiltInDisplayCutout"> - M 687.0,0 - l -66,50 - l 0,50 - l 66,50 - l 66,0 - l 66,-50 - l 0,-50 - l -66,-50 - z + M 0,0 + L -24, 0 + L -21.9940446283, 20.0595537175 + C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0 + L 8.8, 32.0 + C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175 + L 24, 0 + Z + @dp </string> <!-- Whether the display cutout region of the main built-in display should be forced to @@ -37,7 +46,7 @@ <bool name="config_fillMainBuiltInDisplayCutout">true</bool> <!-- Height of the status bar --> - <dimen name="status_bar_height">150px</dimen> + <dimen name="status_bar_height">48dp</dimen> </resources> diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 04cee676075d..9b67f8f32704 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5134,15 +5134,20 @@ message MetricsEvent { // OS: P ACTION_SCREENSHOT_POWER_MENU = 1282; - // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access + // OPEN: Settings > Apps & Notifications -> Special app access -> Directory Access // CATEGORY: SETTINGS // OS: P - STORAGE_ACCESS = 1283; + DIRECTORY_ACCESS = 1283; - // OPEN: Settings > Apps & Notifications -> Special app access -> Storage Access -> Package + // OPEN: Settings > Apps & Notifications -> Special app access -> Directory Access -> Package // CATEGORY: SETTINGS // OS: P - APPLICATIONS_STORAGE_DETAIL = 1284; + APPLICATIONS_DIRECTORY_ACCESS_DETAIL = 1284; + + // OPEN: Settings > Battery > Smart Battery > Restricted apps + // CATEGORY: SETTINGS + // OS: P + FUELGAUGE_RESTRICTED_APP_DETAILS = 1285; // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index d817da53f523..7c6019e76416 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -193,6 +193,9 @@ message SystemMessage { // Inform the user that Wifi Wake has automatically re-enabled Wifi NOTE_WIFI_WAKE_TURNED_BACK_ON = 44; + // Inform the user that unexpectedly rapid network usage is happening + NOTE_NET_RAPID = 45; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java index 3b8d4bca0598..672518cc17ed 100644 --- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java @@ -21,6 +21,8 @@ import android.app.StatusBarManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -30,20 +32,34 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ScreenshotHelper; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; +import java.util.function.Supplier; + /** * Handle the back-end of AccessibilityService#performGlobalAction */ public class GlobalActionPerformer { private final WindowManagerInternal mWindowManagerService; private final Context mContext; + private Supplier<ScreenshotHelper> mScreenshotHelperSupplier; public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) { mContext = context; mWindowManagerService = windowManagerInternal; + mScreenshotHelperSupplier = null; + } + + // Used to mock ScreenshotHelper + @VisibleForTesting + public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal, + Supplier<ScreenshotHelper> screenshotHelperSupplier) { + this(context, windowManagerInternal); + mScreenshotHelperSupplier = screenshotHelperSupplier; } public boolean performGlobalAction(int action) { @@ -79,6 +95,9 @@ public class GlobalActionPerformer { case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: { return lockScreen(); } + case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: { + return takeScreenshot(); + } } return false; } finally { @@ -167,4 +186,12 @@ public class GlobalActionPerformer { mWindowManagerService.lockNow(); return true; } + + private boolean takeScreenshot() { + ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null) + ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext); + screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN, + true, true, new Handler(Looper.getMainLooper())); + return true; + } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index cac7fedd0b00..978ed25378e1 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -44,6 +44,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -78,6 +79,7 @@ import com.android.server.autofill.ui.AutoFillUI; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -443,6 +445,18 @@ public final class AutofillManagerService extends SystemService { } } + // Called by Shell command. + public void getScore(@Nullable String algorithmName, @NonNull String value1, + @NonNull String value2, @NonNull RemoteCallback callback) { + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); + + final FieldClassificationStrategy strategy = + new FieldClassificationStrategy(mContext, UserHandle.USER_CURRENT); + + strategy.getScores(callback, algorithmName, null, + Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 }); + } + private void setDebugLocked(boolean debug) { com.android.server.autofill.Helper.sDebug = debug; android.view.autofill.Helper.sDebug = debug; @@ -518,6 +532,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.removeClientLocked(client); + } else if (sVerbose) { + Slog.v(TAG, "removeClient(): no service for " + userId); } } } @@ -574,6 +590,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.getFillEventHistory(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getFillEventHistory(): no service for " + userId); } } @@ -588,6 +606,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserData(): no service for " + userId); } } @@ -602,6 +622,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.setUserData(getCallingUid(), userData); + } else if (sVerbose) { + Slog.v(TAG, "setUserData(): no service for " + userId); } } } @@ -614,6 +636,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.isFieldClassificationEnabled(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); } } @@ -628,24 +652,30 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.getDefaultFieldClassificationAlgorithm(getCallingUid()); - } + } else { + if (sVerbose) { + Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + } + return null; + } } - - return null; } @Override - public List<String> getAvailableFieldClassificationAlgorithms() throws RemoteException { + public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException { final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.getAvailableFieldClassificationAlgorithms(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + } + return null; } } - - return null; } @Override @@ -656,6 +686,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { return service.getServiceComponentName(); + } else if (sVerbose) { + Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); } } @@ -665,15 +697,17 @@ public final class AutofillManagerService extends SystemService { @Override public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback) throws RemoteException { + final int userId = UserHandle.getCallingUserId(); activityToken = Preconditions.checkNotNull(activityToken, "activityToken"); appCallback = Preconditions.checkNotNull(appCallback, "appCallback"); synchronized (mLock) { - final AutofillManagerServiceImpl service = mServicesCache.get( - UserHandle.getCallingUserId()); + final AutofillManagerServiceImpl service = mServicesCache.get(userId); if (service != null) { return service.restoreSession(sessionId, getCallingUid(), activityToken, appCallback); + } else if (sVerbose) { + Slog.v(TAG, "restoreSession(): no service for " + userId); } } @@ -688,6 +722,8 @@ public final class AutofillManagerService extends SystemService { if (service != null) { service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds, value, action, flags); + } else if (sVerbose) { + Slog.v(TAG, "updateSession(): no service for " + userId); } } } @@ -703,6 +739,8 @@ public final class AutofillManagerService extends SystemService { if (service != null) { restart = service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds, value, action, flags); + } else if (sVerbose) { + Slog.v(TAG, "updateOrRestartSession(): no service for " + userId); } } if (restart) { @@ -720,6 +758,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.finishSessionLocked(sessionId, getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "finishSession(): no service for " + userId); } } } @@ -730,6 +770,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.cancelSessionLocked(sessionId, getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "cancelSession(): no service for " + userId); } } } @@ -740,6 +782,8 @@ public final class AutofillManagerService extends SystemService { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.disableOwnedAutofillServicesLocked(Binder.getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "cancelSession(): no service for " + userId); } } } @@ -755,8 +799,12 @@ public final class AutofillManagerService extends SystemService { public boolean isServiceEnabled(int userId, String packageName) { synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service == null) return false; - return Objects.equals(packageName, service.getServicePackageName()); + if (service != null) { + return Objects.equals(packageName, service.getServicePackageName()); + } else if (sVerbose) { + Slog.v(TAG, "isServiceEnabled(): no service for " + userId); + } + return false; } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index da74dba31416..6bcfc4bf5a5e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -43,20 +43,15 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Looper; -import android.os.Parcel; -import android.os.Parcelable; +import android.os.RemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.Parcelable.Creator; -import android.os.RemoteCallback; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; -import android.service.autofill.Dataset; -import android.service.autofill.EditDistanceScorer; import android.service.autofill.FieldClassification; import android.service.autofill.FieldClassification.Match; import android.service.autofill.FillEventHistory; @@ -69,8 +64,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.LocalLog; -import android.util.Log; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -89,7 +82,6 @@ import com.android.server.autofill.ui.AutoFillUI; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Random; @@ -124,137 +116,7 @@ final class AutofillManagerServiceImpl { private final LocalLog mRequestsHistory; private final LocalLog mUiLatencyHistory; - - // TODO(b/70939974): temporary, will be moved to ExtServices - static final class FieldClassificationAlgorithmService { - - static final String EXTRA_SCORES = "scores"; - - /** - * Gets the name of all available algorithms. - */ - @NonNull - public List<String> getAvailableAlgorithms() { - return Arrays.asList(EditDistanceScorer.NAME); - } - - /** - * Gets the default algorithm that's used when an algorithm is not specified or is invalid. - */ - @NonNull - public String getDefaultAlgorithm() { - return EditDistanceScorer.NAME; - } - - /** - * Gets the field classification scores. - * - * @param algorithmName algorithm to be used. If invalid, the default algorithm will be used - * instead. - * @param algorithmArgs optional arguments to be passed to the algorithm. - * @param currentValues values entered by the user. - * @param userValues values from the user data. - * @param callback returns a nullable bundle with the parcelable results on - * {@link #EXTRA_SCORES}. - */ - @Nullable - void getScores(@NonNull String algorithmName, @Nullable Bundle algorithmArgs, - List<AutofillValue> currentValues, @NonNull String[] userValues, - @NonNull RemoteCallback callback) { - if (currentValues == null || userValues == null) { - // TODO(b/70939974): use preconditions / add unit test - throw new IllegalArgumentException("values cannot be null"); - } - if (currentValues.isEmpty() || userValues.length == 0) { - Slog.w(TAG, "getScores(): empty currentvalues (" + currentValues - + ") or userValues (" + Arrays.toString(userValues) + ")"); - // TODO(b/70939974): add unit test - callback.sendResult(null); - } - String actualAlgorithName = algorithmName; - if (!EditDistanceScorer.NAME.equals(algorithmName)) { - Slog.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " - + EditDistanceScorer.NAME + " instead"); - actualAlgorithName = EditDistanceScorer.NAME; - } - final int currentValuesSize = currentValues.size(); - if (sDebug) { - Log.d(TAG, "getScores() will return a " + currentValuesSize + "x" - + userValues.length + " matrix for " + actualAlgorithName); - } - final FieldClassificationScores scores = new FieldClassificationScores( - actualAlgorithName, currentValuesSize, userValues.length); - final EditDistanceScorer algorithm = EditDistanceScorer.getInstance(); - for (int i = 0; i < currentValuesSize; i++) { - for (int j = 0; j < userValues.length; j++) { - final float score = algorithm.getScore(currentValues.get(i), userValues[j]); - scores.scores[i][j] = score; - } - } - final Bundle result = new Bundle(); - result.putParcelable(EXTRA_SCORES, scores); - callback.sendResult(result); - } - } - - // TODO(b/70939974): temporary, will be moved to ExtServices - public static final class FieldClassificationScores implements Parcelable { - public final String algorithmName; - public final float[][] scores; - - public FieldClassificationScores(String algorithmName, int size1, int size2) { - this.algorithmName = algorithmName; - scores = new float[size1][size2]; - } - - public FieldClassificationScores(Parcel parcel) { - algorithmName = parcel.readString(); - final int size1 = parcel.readInt(); - final int size2 = parcel.readInt(); - scores = new float[size1][size2]; - for (int i = 0; i < size1; i++) { - for (int j = 0; j < size2; j++) { - scores[i][j] = parcel.readFloat(); - } - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(algorithmName); - int size1 = scores.length; - int size2 = scores[0].length; - parcel.writeInt(size1); - parcel.writeInt(size2); - for (int i = 0; i < size1; i++) { - for (int j = 0; j < size2; j++) { - parcel.writeFloat(scores[i][j]); - } - } - } - - public static final Creator<FieldClassificationScores> CREATOR = new Creator<FieldClassificationScores>() { - - @Override - public FieldClassificationScores createFromParcel(Parcel parcel) { - return new FieldClassificationScores(parcel); - } - - @Override - public FieldClassificationScores[] newArray(int size) { - return new FieldClassificationScores[size]; - } - - }; - } - - private final FieldClassificationAlgorithmService mFcService = - new FieldClassificationAlgorithmService(); + private final FieldClassificationStrategy mFieldClassificationStrategy; /** * Apps disabled by the service; key is package name, value is when they will be enabled again. @@ -324,6 +186,7 @@ final class AutofillManagerServiceImpl { mUiLatencyHistory = uiLatencyHistory; mUserId = userId; mUi = ui; + mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId); updateLocked(disabled); } @@ -1089,10 +952,8 @@ final class AutofillManagerServiceImpl { mUserData.dump(prefix2, pw); } - pw.print(prefix); pw.print("Available Field Classification algorithms: "); - pw.println(mFcService.getAvailableAlgorithms()); - pw.print(prefix); pw.print("Default Field Classification algorithm: "); - pw.println(mFcService.getDefaultAlgorithm()); + pw.print(prefix); pw.println("Field Classification strategy: "); + mFieldClassificationStrategy.dump(prefix2, pw); } void destroySessionsLocked() { @@ -1288,17 +1149,17 @@ final class AutofillManagerServiceImpl { mUserId) == 1; } - FieldClassificationAlgorithmService getFieldClassificationService() { - return mFcService; + FieldClassificationStrategy getFieldClassificationStrategy() { + return mFieldClassificationStrategy; } - List<String> getAvailableFieldClassificationAlgorithms(int callingUid) { + String[] getAvailableFieldClassificationAlgorithms(int callingUid) { synchronized (mLock) { if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) { return null; } } - return mFcService.getAvailableAlgorithms(); + return mFieldClassificationStrategy.getAvailableAlgorithms(); } String getDefaultFieldClassificationAlgorithm(int callingUid) { @@ -1307,7 +1168,7 @@ final class AutofillManagerServiceImpl { return null; } } - return mFcService.getDefaultAlgorithm(); + return mFieldClassificationStrategy.getDefaultAlgorithm(); } @Override diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java index f3de557ec540..4d69ef952f72 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java @@ -16,11 +16,15 @@ package com.android.server.autofill; +import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; + import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS; import android.os.Bundle; +import android.os.RemoteCallback; import android.os.ShellCommand; import android.os.UserHandle; +import android.service.autofill.AutofillFieldClassificationService.Scores; import android.view.autofill.AutofillManager; import com.android.internal.os.IResultReceiver; @@ -80,13 +84,16 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" Sets the maximum number of partitions per session."); pw.println(""); pw.println(" list sessions [--user USER_ID]"); - pw.println(" List all pending sessions."); + pw.println(" Lists all pending sessions."); pw.println(""); pw.println(" destroy sessions [--user USER_ID]"); - pw.println(" Destroy all pending sessions."); + pw.println(" Destroys all pending sessions."); pw.println(""); pw.println(" reset"); - pw.println(" Reset all pending sessions and cached service connections."); + pw.println(" Resets all pending sessions and cached service connections."); + pw.println(""); + pw.println(" get fc_score [--algorithm ALGORITHM] value1 value2"); + pw.println(" Gets the field classification score for 2 fields."); pw.println(""); } } @@ -98,6 +105,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return getLogLevel(pw); case "max_partitions": return getMaxPartitions(pw); + case "fc_score": + return getFieldClassificationScore(pw); default: pw.println("Invalid set: " + what); return -1; @@ -164,6 +173,32 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return 0; } + private int getFieldClassificationScore(PrintWriter pw) { + final String nextArg = getNextArgRequired(); + final String algorithm, value1; + if ("--algorithm".equals(nextArg)) { + algorithm = getNextArgRequired(); + value1 = getNextArgRequired(); + } else { + algorithm = null; + value1 = nextArg; + } + final String value2 = getNextArgRequired(); + + final CountDownLatch latch = new CountDownLatch(1); + mService.getScore(algorithm, value1, value2, new RemoteCallback((result) -> { + final Scores scores = result.getParcelable(EXTRA_SCORES); + if (scores == null) { + pw.println("no score"); + } else { + pw.println(scores.scores[0][0]); + } + latch.countDown(); + })); + + return waitForLatch(pw, latch); + } + private int requestDestroy(PrintWriter pw) { if (!isNextArgSessions(pw)) { return -1; @@ -210,19 +245,13 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return true; } - private boolean isNextArgLogLevel(PrintWriter pw, String cmd) { - final String type = getNextArgRequired(); - if (!type.equals("log_level")) { - pw.println("Error: invalid " + cmd + " type: " + type); - return false; - } - return true; - } - private int requestSessionCommon(PrintWriter pw, CountDownLatch latch, Runnable command) { command.run(); + return waitForLatch(pw, latch); + } + private int waitForLatch(PrintWriter pw, CountDownLatch latch) { try { final boolean received = latch.await(5, TimeUnit.SECONDS); if (!received) { diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java new file mode 100644 index 000000000000..da5220104e3c --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java @@ -0,0 +1,289 @@ +/* + * 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 com.android.server.autofill; + +import static android.view.autofill.AutofillManager.FC_SERVICE_TIMEOUT; + +import static com.android.server.autofill.Helper.sDebug; +import static com.android.server.autofill.Helper.sVerbose; +import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; +import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM; + +import android.Manifest; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.autofill.AutofillFieldClassificationService; +import android.service.autofill.IAutofillFieldClassificationService; +import android.util.Log; +import android.util.Slog; +import android.view.autofill.AutofillValue; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Strategy used to bridge the field classification algorithms provided by a service in an external + * package. + */ +//TODO(b/70291841): add unit tests ? +final class FieldClassificationStrategy { + + private static final String TAG = "FieldClassificationStrategy"; + + private final Context mContext; + private final Object mLock = new Object(); + private final int mUserId; + + @GuardedBy("mLock") + private ServiceConnection mServiceConnection; + + @GuardedBy("mLock") + private IAutofillFieldClassificationService mRemoteService; + + @GuardedBy("mLock") + private ArrayList<Command> mQueuedCommands; + + public FieldClassificationStrategy(Context context, int userId) { + mContext = context; + mUserId = userId; + } + + @Nullable + private ServiceInfo getServiceInfo() { + final String packageName = + mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); + if (packageName == null) { + Slog.w(TAG, "no external services package!"); + return null; + } + + final Intent intent = new Intent(AutofillFieldClassificationService.SERVICE_INTERFACE); + intent.setPackage(packageName); + final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Slog.w(TAG, "No valid components found."); + return null; + } + return resolveInfo.serviceInfo; + } + + @Nullable + private ComponentName getServiceComponentName() { + final ServiceInfo serviceInfo = getServiceInfo(); + if (serviceInfo == null) return null; + + final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); + if (!Manifest.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE + .equals(serviceInfo.permission)) { + Slog.w(TAG, name.flattenToShortString() + " does not require permission " + + Manifest.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE); + return null; + } + + if (sVerbose) Slog.v(TAG, "getServiceComponentName(): " + name); + return name; + } + + /** + * Run a command, starting the service connection if necessary. + */ + private void connectAndRun(@NonNull Command command) { + synchronized (mLock) { + if (mRemoteService != null) { + try { + if (sVerbose) Slog.v(TAG, "running command right away"); + command.run(mRemoteService); + } catch (RemoteException e) { + Slog.w(TAG, "exception calling service: " + e); + } + return; + } else { + if (sDebug) Slog.d(TAG, "service is null; queuing command"); + if (mQueuedCommands == null) { + mQueuedCommands = new ArrayList<>(1); + } + mQueuedCommands.add(command); + // If we're already connected, don't create a new connection, just leave - the + // command will be run when the service connects + if (mServiceConnection != null) return; + } + + if (sVerbose) Slog.v(TAG, "creating connection"); + + // Create the connection + mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (sVerbose) Slog.v(TAG, "onServiceConnected(): " + name); + synchronized (mLock) { + mRemoteService = IAutofillFieldClassificationService.Stub + .asInterface(service); + if (mQueuedCommands != null) { + final int size = mQueuedCommands.size(); + if (sDebug) Slog.d(TAG, "running " + size + " queued commands"); + for (int i = 0; i < size; i++) { + final Command queuedCommand = mQueuedCommands.get(i); + try { + if (sVerbose) Slog.v(TAG, "running queued command #" + i); + queuedCommand.run(mRemoteService); + } catch (RemoteException e) { + Slog.w(TAG, "exception calling " + name + ": " + e); + } + } + mQueuedCommands = null; + } else if (sDebug) Slog.d(TAG, "no queued commands"); + } + } + + @Override + @MainThread + public void onServiceDisconnected(ComponentName name) { + if (sVerbose) Slog.v(TAG, "onServiceDisconnected(): " + name); + synchronized (mLock) { + mRemoteService = null; + } + } + + @Override + public void onBindingDied(ComponentName name) { + if (sVerbose) Slog.v(TAG, "onBindingDied(): " + name); + synchronized (mLock) { + mRemoteService = null; + } + } + + @Override + public void onNullBinding(ComponentName name) { + if (sVerbose) Slog.v(TAG, "onNullBinding(): " + name); + synchronized (mLock) { + mRemoteService = null; + } + } + }; + + final ComponentName component = getServiceComponentName(); + if (sVerbose) Slog.v(TAG, "binding to: " + component); + if (component != null) { + final Intent intent = new Intent(); + intent.setComponent(component); + final long token = Binder.clearCallingIdentity(); + try { + mContext.bindServiceAsUser(intent, mServiceConnection, Context.BIND_AUTO_CREATE, + UserHandle.of(mUserId)); + if (sVerbose) Slog.v(TAG, "bound"); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + } + + /** + * Gets the name of all available algorithms. + */ + @Nullable + String[] getAvailableAlgorithms() { + return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS, + (res, id) -> res.getStringArray(id)); + } + + /** + * Gets the default algorithm that's used when an algorithm is not specified or is invalid. + */ + @Nullable + String getDefaultAlgorithm() { + return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id)); + } + + @Nullable + private <T> T getMetadataValue(String field, MetadataParser<T> parser) { + final ServiceInfo serviceInfo = getServiceInfo(); + if (serviceInfo == null) return null; + + final PackageManager pm = mContext.getPackageManager(); + + final Resources res; + try { + res = pm.getResourcesForApplication(serviceInfo.applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Error getting application resources for " + serviceInfo, e); + return null; + } + + final int resourceId = serviceInfo.metaData.getInt(field); + return parser.get(res, resourceId); + } + + //TODO(b/70291841): rename this method (and all others in the chain) to something like + // calculateScores() ? + void getScores(RemoteCallback callback, @Nullable String algorithmName, + @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues, + @NonNull String[] userDataValues) { + connectAndRun((service) -> service.getScores(callback, algorithmName, + algorithmArgs, actualValues, userDataValues)); + } + + void dump(String prefix, PrintWriter pw) { + final ComponentName impl = getServiceComponentName(); + pw.print(prefix); pw.print("User ID: "); pw.println(mUserId); + pw.print(prefix); pw.print("Queued commands: "); + if (mQueuedCommands == null) { + pw.println("N/A"); + } else { + pw.println(mQueuedCommands.size()); + } + pw.print(prefix); pw.print("Implementation: "); + if (impl == null) { + pw.println("N/A"); + return; + } + pw.println(impl.flattenToShortString()); + + pw.print(prefix); pw.print("Available algorithms: "); + pw.println(Arrays.toString(getAvailableAlgorithms())); + pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm()); + } + + private static interface Command { + void run(IAutofillFieldClassificationService service) throws RemoteException; + } + + private static interface MetadataParser<T> { + T get(Resources res, int resId); + } +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index f5d1336a0f6e..63f83849db34 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,7 @@ package com.android.server.autofill; import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE; +import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; @@ -25,7 +26,6 @@ import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; -import static com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationAlgorithmService.EXTRA_SCORES; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sPartitionMaxCount; import static com.android.server.autofill.Helper.sVerbose; @@ -54,11 +54,11 @@ import android.os.Parcelable; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; +import android.service.autofill.AutofillFieldClassificationService.Scores; import android.service.autofill.AutofillService; import android.service.autofill.Dataset; import android.service.autofill.FieldClassification; import android.service.autofill.FieldClassification.Match; -import android.service.carrier.CarrierMessagingService.ResultCallback; import android.service.autofill.FillContext; import android.service.autofill.FillRequest; import android.service.autofill.FillResponse; @@ -86,8 +86,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; import com.android.internal.util.ArrayUtils; -import com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationAlgorithmService; -import com.android.server.autofill.AutofillManagerServiceImpl.FieldClassificationScores; import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.PendingUi; @@ -99,7 +97,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; /** * A session for a given activity. @@ -1101,10 +1098,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Sets field classification scores - final FieldClassificationAlgorithmService fcService = - mService.getFieldClassificationService(); - if (userData != null && fcService != null) { - logFieldClassificationScoreLocked(fcService, ignoredDatasets, changedFieldIds, + final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy(); + if (userData != null && fcStrategy != null) { + logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, manuallyFilledIds, userData, mViewStates.values()); @@ -1121,7 +1117,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ private void logFieldClassificationScoreLocked( - @NonNull AutofillManagerServiceImpl.FieldClassificationAlgorithmService fcService, + @NonNull FieldClassificationStrategy fcStrategy, @NonNull ArraySet<String> ignoredDatasets, @NonNull ArrayList<AutofillId> changedFieldIds, @NonNull ArrayList<String> changedDatasetIds, @@ -1161,6 +1157,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState fieldIds[k++] = viewState.id; } + // Then use the results, asynchronously final RemoteCallback callback = new RemoteCallback((result) -> { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); @@ -1170,35 +1167,43 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName.getPackageName()); return; } - final FieldClassificationScores matrix = result.getParcelable(EXTRA_SCORES); - - // Then use the results. - for (int i = 0; i < viewsSize; i++) { - final AutofillId fieldId = fieldIds[i]; - - ArrayList<Match> matches = null; - for (int j = 0; j < userValues.length; j++) { - String remoteId = remoteIds[j]; - final String actualAlgorithm = matrix.algorithmName; - final float score = matrix.scores[i][j]; - if (score > 0) { - if (sVerbose) { - Slog.v(TAG, "adding score " + score + " at index " + j + " and id " - + fieldId); + final Scores scores = result.getParcelable(EXTRA_SCORES); + if (scores == null) { + Slog.w(TAG, "No field classification score on " + result); + return; + } + int i = 0, j = 0; + try { + for (i = 0; i < viewsSize; i++) { + final AutofillId fieldId = fieldIds[i]; + + ArrayList<Match> matches = null; + for (j = 0; j < userValues.length; j++) { + String remoteId = remoteIds[j]; + final float score = scores.scores[i][j]; + if (score > 0) { + if (sVerbose) { + Slog.v(TAG, "adding score " + score + " at index " + j + " and id " + + fieldId); + } + if (matches == null) { + matches = new ArrayList<>(userValues.length); + } + matches.add(new Match(remoteId, score)); } - if (matches == null) { - matches = new ArrayList<>(userValues.length); + else if (sVerbose) { + Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId); } - matches.add(new Match(remoteId, score, actualAlgorithm)); } - else if (sVerbose) { - Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId); + if (matches != null) { + detectedFieldIds.add(fieldId); + detectedFieldClassifications.add(new FieldClassification(matches)); } } - if (matches != null) { - detectedFieldIds.add(fieldId); - detectedFieldClassifications.add(new FieldClassification(matches)); - } + } catch (ArrayIndexOutOfBoundsException e) { + Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": " + + Arrays.toString(scores.scores), e); + return; } mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, @@ -1207,7 +1212,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName.getPackageName()); }); - fcService.getScores(algorithm, algorithmArgs, currentValues, userValues, callback); + fcStrategy.getScores(callback, algorithm, algorithmArgs, currentValues, userValues); } /** diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 307f74d36ac5..f96fa7c237f7 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -231,8 +231,7 @@ final class SaveUi { final Window window = mDialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); - window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM + window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java index 518891006b37..465bb09927a5 100644 --- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java @@ -3464,16 +3464,21 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "isAppEligibleForBackup"); - String callerLogString = "BMS.isAppEligibleForBackup"; - TransportClient transportClient = - mTransportManager.getCurrentTransportClient(callerLogString); - boolean eligible = - AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager); - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + long oldToken = Binder.clearCallingIdentity(); + try { + String callerLogString = "BMS.isAppEligibleForBackup"; + TransportClient transportClient = + mTransportManager.getCurrentTransportClient(callerLogString); + boolean eligible = + AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport( + transportClient, packageName, mPackageManager); + if (transportClient != null) { + mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + } + return eligible; + } finally { + Binder.restoreCallingIdentity(oldToken); } - return eligible; } @Override @@ -3481,21 +3486,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup"); - String callerLogString = "BMS.filterAppsEligibleForBackup"; - TransportClient transportClient = - mTransportManager.getCurrentTransportClient(callerLogString); - List<String> eligibleApps = new LinkedList<>(); - for (String packageName : packages) { - if (AppBackupUtils - .appIsRunningAndEligibleForBackupWithTransport( - transportClient, packageName, mPackageManager)) { - eligibleApps.add(packageName); + long oldToken = Binder.clearCallingIdentity(); + try { + String callerLogString = "BMS.filterAppsEligibleForBackup"; + TransportClient transportClient = + mTransportManager.getCurrentTransportClient(callerLogString); + List<String> eligibleApps = new LinkedList<>(); + for (String packageName : packages) { + if (AppBackupUtils + .appIsRunningAndEligibleForBackupWithTransport( + transportClient, packageName, mPackageManager)) { + eligibleApps.add(packageName); + } } + if (transportClient != null) { + mTransportManager.disposeOfTransportClient(transportClient, callerLogString); + } + return eligibleApps.toArray(new String[eligibleApps.size()]); + } finally { + Binder.restoreCallingIdentity(oldToken); } - if (transportClient != null) { - mTransportManager.disposeOfTransportClient(transportClient, callerLogString); - } - return eligibleApps.toArray(new String[eligibleApps.size()]); } @Override @@ -3514,6 +3524,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } else if ("agents".startsWith(arg)) { dumpAgents(pw); return; + } else if ("transportclients".equals(arg.toLowerCase())) { + mTransportManager.dump(pw); + return; } } } @@ -3576,6 +3589,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } + mTransportManager.dump(pw); + pw.println("Pending init: " + mPendingInits.size()); for (String s : mPendingInits) { pw.println(" " + s); diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java index 30fd25a92484..7e179e5d24f8 100644 --- a/services/backup/java/com/android/server/backup/TransportManager.java +++ b/services/backup/java/com/android/server/backup/TransportManager.java @@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -42,13 +43,12 @@ import com.android.server.backup.transport.TransportConnectionListener; import com.android.server.backup.transport.TransportNotAvailableException; import com.android.server.backup.transport.TransportNotRegisteredException; +import java.io.PrintWriter; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** Handles in-memory bookkeeping of all BackupTransport objects. */ public class TransportManager { @@ -62,9 +62,17 @@ public class TransportManager { private final PackageManager mPackageManager; private final Set<ComponentName> mTransportWhitelist; private final TransportClientManager mTransportClientManager; - private final Object mTransportLock = new Object(); private OnTransportRegisteredListener mOnTransportRegisteredListener = (c, n) -> {}; + /** + * Lock for registered transports and currently selected transport. + * + * <p><b>Warning:</b> No calls to {@link IBackupTransport} or calls that result in transport + * code being executed such as {@link TransportClient#connect(String)}} and its variants should + * be made with this lock held, risk of deadlock. + */ + private final Object mTransportLock = new Object(); + /** @see #getRegisteredTransportNames() */ @GuardedBy("mTransportLock") private final Map<ComponentName, TransportDescription> mRegisteredTransportsDescriptionMap = @@ -109,15 +117,16 @@ public class TransportManager { @WorkerThread void onPackageChanged(String packageName, String... components) { + // Unfortunately this can't be atomic because we risk a deadlock if + // registerTransportsFromPackage() is put inside the synchronized block + Set<ComponentName> transportComponents = new ArraySet<>(components.length); + for (String componentName : components) { + transportComponents.add(new ComponentName(packageName, componentName)); + } synchronized (mTransportLock) { - Set<ComponentName> transportComponents = - Stream.of(components) - .map(component -> new ComponentName(packageName, component)) - .collect(Collectors.toSet()); - mRegisteredTransportsDescriptionMap.keySet().removeIf(transportComponents::contains); - registerTransportsFromPackage(packageName, transportComponents::contains); } + registerTransportsFromPackage(packageName, transportComponents::contains); } /** @@ -142,11 +151,13 @@ public class TransportManager { */ String[] getRegisteredTransportNames() { synchronized (mTransportLock) { - return mRegisteredTransportsDescriptionMap - .values() - .stream() - .map(transportDescription -> transportDescription.name) - .toArray(String[]::new); + String[] transportNames = new String[mRegisteredTransportsDescriptionMap.size()]; + int i = 0; + for (TransportDescription description : mRegisteredTransportsDescriptionMap.values()) { + transportNames[i] = description.name; + i++; + } + return transportNames; } } @@ -263,6 +274,9 @@ public class TransportManager { * This is called with an internal lock held, ensuring that the transport will remain registered * while {@code transportConsumer} is being executed. Don't do heavy operations in {@code * transportConsumer}. + * + * <p><b>Warning:</b> Do NOT make any calls to {@link IBackupTransport} or call any variants of + * {@link TransportClient#connect(String)} here, otherwise you risk deadlock. */ public void forEachRegisteredTransport(Consumer<String> transportConsumer) { synchronized (mTransportLock) { @@ -465,20 +479,27 @@ public class TransportManager { */ @WorkerThread public int registerAndSelectTransport(ComponentName transportComponent) { + // If it's already registered we select and return synchronized (mTransportLock) { - if (!mRegisteredTransportsDescriptionMap.containsKey(transportComponent)) { - int result = registerTransport(transportComponent); - if (result != BackupManager.SUCCESS) { - return result; - } + try { + selectTransport(getTransportName(transportComponent)); + return BackupManager.SUCCESS; + } catch (TransportNotRegisteredException e) { + // Fall through and release lock } + } + // We can't call registerTransport() with the transport lock held + int result = registerTransport(transportComponent); + if (result != BackupManager.SUCCESS) { + return result; + } + synchronized (mTransportLock) { try { selectTransport(getTransportName(transportComponent)); return BackupManager.SUCCESS; } catch (TransportNotRegisteredException e) { - // Shouldn't happen because we are holding the lock - Slog.wtf(TAG, "Transport unexpectedly not registered"); + Slog.wtf(TAG, "Transport got unregistered"); return BackupManager.ERROR_TRANSPORT_UNAVAILABLE; } } @@ -512,13 +533,11 @@ public class TransportManager { if (hosts == null) { return; } - synchronized (mTransportLock) { - for (ResolveInfo host : hosts) { - ComponentName transportComponent = host.serviceInfo.getComponentName(); - if (transportComponentFilter.test(transportComponent) - && isTransportTrusted(transportComponent)) { - registerTransport(transportComponent); - } + for (ResolveInfo host : hosts) { + ComponentName transportComponent = host.serviceInfo.getComponentName(); + if (transportComponentFilter.test(transportComponent) + && isTransportTrusted(transportComponent)) { + registerTransport(transportComponent); } } } @@ -547,22 +566,24 @@ public class TransportManager { /** * Tries to register transport represented by {@code transportComponent}. * + * <p><b>Warning:</b> Don't call this with the transport lock held. + * * @param transportComponent Host of the transport that we want to register. * @return One of {@link BackupManager#SUCCESS}, {@link BackupManager#ERROR_TRANSPORT_INVALID} * or {@link BackupManager#ERROR_TRANSPORT_UNAVAILABLE}. */ @WorkerThread private int registerTransport(ComponentName transportComponent) { + checkCanUseTransport(); + if (!isTransportTrusted(transportComponent)) { return BackupManager.ERROR_TRANSPORT_INVALID; } String transportString = transportComponent.flattenToShortString(); - String callerLogString = "TransportManager.registerTransport()"; TransportClient transportClient = mTransportClientManager.getTransportClient(transportComponent, callerLogString); - final IBackupTransport transport; try { transport = transportClient.connectOrThrow(callerLogString); @@ -593,20 +614,30 @@ public class TransportManager { /** If {@link RemoteException} is thrown the transport is guaranteed to not be registered. */ private void registerTransport(ComponentName transportComponent, IBackupTransport transport) throws RemoteException { + checkCanUseTransport(); + + TransportDescription description = + new TransportDescription( + transport.name(), + transport.transportDirName(), + transport.configurationIntent(), + transport.currentDestinationString(), + transport.dataManagementIntent(), + transport.dataManagementLabel()); synchronized (mTransportLock) { - String name = transport.name(); - TransportDescription description = - new TransportDescription( - name, - transport.transportDirName(), - transport.configurationIntent(), - transport.currentDestinationString(), - transport.dataManagementIntent(), - transport.dataManagementLabel()); mRegisteredTransportsDescriptionMap.put(transportComponent, description); } } + private void checkCanUseTransport() { + Preconditions.checkState( + !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held"); + } + + public void dump(PrintWriter pw) { + mTransportClientManager.dump(pw); + } + private static Predicate<ComponentName> fromPackageFilter(String packageName) { return transportComponent -> packageName.equals(transportComponent.getPackageName()); } diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java index 399f338d26b2..7b2e3df67942 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportClient.java +++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java @@ -16,6 +16,8 @@ package com.android.server.backup.transport; +import static com.android.server.backup.transport.TransportUtils.formatMessage; + import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.WorkerThread; @@ -28,6 +30,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; @@ -41,6 +44,9 @@ import com.android.server.backup.TransportManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -65,6 +71,7 @@ import java.util.concurrent.ExecutionException; */ public class TransportClient { private static final String TAG = "TransportClient"; + private static final int LOG_BUFFER_SIZE = 5; private final Context mContext; private final Intent mBindIntent; @@ -73,6 +80,10 @@ public class TransportClient { private final Handler mListenerHandler; private final String mPrefixForLog; private final Object mStateLock = new Object(); + private final Object mLogBufferLock = new Object(); + + @GuardedBy("mLogBufferLock") + private final List<String> mLogBuffer = new LinkedList<>(); @GuardedBy("mStateLock") private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>(); @@ -112,7 +123,7 @@ public class TransportClient { // For logging String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", ""); - mPrefixForLog = classNameForLog + "#" + mIdentifier + ": "; + mPrefixForLog = classNameForLog + "#" + mIdentifier + ":"; } public ComponentName getTransportComponent() { @@ -229,7 +240,7 @@ public class TransportClient { switch (mState) { case State.UNUSABLE: - log(Log.DEBUG, caller, "Async connect: UNUSABLE client"); + log(Log.WARN, caller, "Async connect: UNUSABLE client"); notifyListener(listener, null, caller); break; case State.IDLE: @@ -324,14 +335,14 @@ public class TransportClient { IBackupTransport transport = mTransport; if (transport != null) { - log(Log.DEBUG, caller, "Sync connect: reusing transport"); + log(Log.INFO, caller, "Sync connect: reusing transport"); return transport; } // If it's already UNUSABLE we return straight away, no need to go to main-thread synchronized (mStateLock) { if (mState == State.UNUSABLE) { - log(Log.DEBUG, caller, "Sync connect: UNUSABLE client"); + log(Log.WARN, caller, "Sync connect: UNUSABLE client"); return null; } } @@ -403,13 +414,16 @@ public class TransportClient { } private void notifyListener( - TransportConnectionListener listener, IBackupTransport transport, String caller) { - log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport); + TransportConnectionListener listener, + @Nullable IBackupTransport transport, + String caller) { + String transportString = (transport != null) ? "IBackupTransport" : "null"; + log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString); mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this)); } @GuardedBy("mStateLock") - private void notifyListenersAndClearLocked(IBackupTransport transport) { + private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) { for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) { TransportConnectionListener listener = entry.getKey(); String caller = entry.getValue(); @@ -509,13 +523,30 @@ public class TransportClient { } private void log(int priority, String message) { - TransportUtils.log(priority, TAG, message); + TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message)); + saveLogEntry(formatMessage(null, null, message)); } - private void log(int priority, String caller, String msg) { - TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg); - // TODO(brufino): Log in internal list for dump - // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis()); + private void log(int priority, String caller, String message) { + TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message)); + saveLogEntry(formatMessage(null, caller, message)); + } + + private void saveLogEntry(String message) { + CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis()); + message = time + " " + message; + synchronized (mLogBufferLock) { + if (mLogBuffer.size() == LOG_BUFFER_SIZE) { + mLogBuffer.remove(mLogBuffer.size() - 1); + } + mLogBuffer.add(0, message); + } + } + + List<String> getLogBuffer() { + synchronized (mLogBufferLock) { + return Collections.unmodifiableList(mLogBuffer); + } } @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP}) diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java index 1cbe74716b03..1132bce612b7 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java +++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java @@ -17,19 +17,20 @@ package com.android.server.backup.transport; import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST; +import static com.android.server.backup.transport.TransportUtils.formatMessage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.util.Log; - import com.android.server.backup.TransportManager; +import java.io.PrintWriter; +import java.util.Map; +import java.util.WeakHashMap; /** * Manages the creation and disposal of {@link TransportClient}s. The only class that should use * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}. - * - * <p>TODO(brufino): Implement pool of TransportClients */ public class TransportClientManager { private static final String TAG = "TransportClientManager"; @@ -37,6 +38,7 @@ public class TransportClientManager { private final Context mContext; private final Object mTransportClientsLock = new Object(); private int mTransportClientsCreated = 0; + private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>(); public TransportClientManager(Context context) { mContext = context; @@ -62,8 +64,10 @@ public class TransportClientManager { bindIntent, transportComponent, Integer.toString(mTransportClientsCreated)); + mTransportClientsCallerMap.put(transportClient, caller); mTransportClientsCreated++; - TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient); + TransportUtils.log( + Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient)); return transportClient; } } @@ -77,7 +81,25 @@ public class TransportClientManager { * details. */ public void disposeOfTransportClient(TransportClient transportClient, String caller) { - TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient); transportClient.unbind(caller); + synchronized (mTransportClientsLock) { + TransportUtils.log( + Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient)); + mTransportClientsCallerMap.remove(transportClient); + } + } + + public void dump(PrintWriter pw) { + pw.println("Transport clients created: " + mTransportClientsCreated); + synchronized (mTransportClientsLock) { + pw.println("Current transport clients: " + mTransportClientsCallerMap.size()); + for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) { + String caller = mTransportClientsCallerMap.get(transportClient); + pw.println(" " + transportClient + " [" + caller + "]"); + for (String logEntry : transportClient.getLogBuffer()) { + pw.println(" " + logEntry); + } + } + } } } diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java index 92bba9bf06f0..56b2d44ec420 100644 --- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java +++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java @@ -41,21 +41,20 @@ public class TransportUtils { } static void log(int priority, String tag, String message) { - log(priority, tag, null, message); - } - - static void log(int priority, String tag, @Nullable String caller, String message) { - log(priority, tag, "", caller, message); + if (Log.isLoggable(tag, priority)) { + Slog.println(priority, tag, message); + } } - static void log( - int priority, String tag, String prefix, @Nullable String caller, String message) { - if (Log.isLoggable(tag, priority)) { - if (caller != null) { - prefix += "[" + caller + "] "; - } - Slog.println(priority, tag, prefix + message); + static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) { + StringBuilder string = new StringBuilder(); + if (prefix != null) { + string.append(prefix).append(" "); + } + if (caller != null) { + string.append("[").append(caller).append("] "); } + return string.append(message).toString(); } private TransportUtils() {} diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 4ffa5f1f38f9..f4675fd3316d 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -1316,14 +1316,8 @@ public class AppOpsService extends IAppOpsService.Stub { isPrivileged = (appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; } else { - if ("media".equals(packageName)) { - pkgUid = Process.MEDIA_UID; - isPrivileged = false; - } else if ("audioserver".equals(packageName)) { - pkgUid = Process.AUDIOSERVER_UID; - isPrivileged = false; - } else if ("cameraserver".equals(packageName)) { - pkgUid = Process.CAMERASERVER_UID; + pkgUid = resolveUid(packageName); + if (pkgUid >= 0) { isPrivileged = false; } } @@ -1957,9 +1951,8 @@ public class AppOpsService extends IAppOpsService.Stub { if (nonpackageUid != -1) { packageName = null; } else { - if ("root".equals(packageName)) { - packageUid = 0; - } else { + packageUid = resolveUid(packageName); + if (packageUid < 0) { packageUid = AppGlobals.getPackageManager().getPackageUid(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); } @@ -2052,6 +2045,10 @@ public class AppOpsService extends IAppOpsService.Stub { } if (ops == null || ops.size() <= 0) { pw.println("No operations."); + if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) { + pw.println("Default mode: " + AppOpsManager.modeToString( + AppOpsManager.opToDefaultMode(shell.op))); + } return 0; } final long now = System.currentTimeMillis(); @@ -2061,24 +2058,7 @@ public class AppOpsService extends IAppOpsService.Stub { AppOpsManager.OpEntry ent = entries.get(j); pw.print(AppOpsManager.opToName(ent.getOp())); pw.print(": "); - switch (ent.getMode()) { - case AppOpsManager.MODE_ALLOWED: - pw.print("allow"); - break; - case AppOpsManager.MODE_IGNORED: - pw.print("ignore"); - break; - case AppOpsManager.MODE_ERRORED: - pw.print("deny"); - break; - case AppOpsManager.MODE_DEFAULT: - pw.print("default"); - break; - default: - pw.print("mode="); - pw.print(ent.getMode()); - break; - } + pw.print(AppOpsManager.modeToString(ent.getMode())); if (ent.getTime() != 0) { pw.print("; time="); TimeUtils.formatDuration(now - ent.getTime(), pw); @@ -2563,16 +2543,41 @@ public class AppOpsService extends IAppOpsService.Stub { } private static String resolvePackageName(int uid, String packageName) { - if (uid == 0) { + if (uid == Process.ROOT_UID) { return "root"; } else if (uid == Process.SHELL_UID) { return "com.android.shell"; + } else if (uid == Process.MEDIA_UID) { + return "media"; + } else if (uid == Process.AUDIOSERVER_UID) { + return "audioserver"; + } else if (uid == Process.CAMERASERVER_UID) { + return "cameraserver"; } else if (uid == Process.SYSTEM_UID && packageName == null) { return "android"; } return packageName; } + private static int resolveUid(String packageName) { + if (packageName == null) { + return -1; + } + switch (packageName) { + case "root": + return Process.ROOT_UID; + case "shell": + return Process.SHELL_UID; + case "media": + return Process.MEDIA_UID; + case "audioserver": + return Process.AUDIOSERVER_UID; + case "cameraserver": + return Process.CAMERASERVER_UID; + } + return -1; + } + private static String[] getPackagesForUid(int uid) { String[] packageNames = null; try { diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 04d292fa1ae4..dc5f5a270748 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -383,16 +383,16 @@ public final class BatteryService extends SystemService { } } - private void update(HealthInfo info) { + private void update(android.hardware.health.V2_0.HealthInfo info) { traceBegin("HealthInfoUpdate"); synchronized (mLock) { if (!mUpdatesStopped) { - mHealthInfo = info; + mHealthInfo = info.legacy; // Process the new values. processValuesLocked(false); mLock.notifyAll(); // for any waiters on new info } else { - copy(mLastHealthInfo, info); + copy(mLastHealthInfo, info.legacy); } } traceEnd(); @@ -1010,7 +1010,7 @@ public final class BatteryService extends SystemService { private final class HealthHalCallback extends IHealthInfoCallback.Stub implements HealthServiceWrapper.Callback { - @Override public void healthInfoChanged(HealthInfo props) { + @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) { BatteryService.this.update(props); } // on new service registered diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 337406d58f9d..20777901a3aa 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -439,10 +439,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.AIRPLANE_MODE_ON, 0) == 1; } + private boolean supportBluetoothPersistedState() { + return mContext.getResources().getBoolean(R.bool.config_supportBluetoothPersistedState); + } + /** * Returns true if the Bluetooth saved state is "on" */ private boolean isBluetoothPersistedStateOn() { + if (!supportBluetoothPersistedState()) { + return false; + } int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); if (DBG) { Slog.d(TAG, "Bluetooth persisted state: " + state); @@ -454,6 +461,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ private boolean isBluetoothPersistedStateOnBluetooth() { + if (!supportBluetoothPersistedState()) { + return false; + } return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH; } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 77521df7e06a..c1f4b789e002 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -63,6 +63,7 @@ import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; +import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; @@ -131,6 +132,7 @@ import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.DnsManager; +import com.android.server.connectivity.DnsManager.PrivateDnsConfig; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; @@ -400,6 +402,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_REVALIDATE_NETWORK = 36; + // Handle changes in Private DNS settings. + private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37; + private static String eventName(int what) { return sMagicDecoderRing.get(what, Integer.toString(what)); } @@ -886,6 +891,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mMultinetworkPolicyTracker.start(); mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties); + registerPrivateDnsSettingsCallbacks(); } private Tethering makeTethering() { @@ -946,6 +952,12 @@ public class ConnectivityService extends IConnectivityManager.Stub EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); } + private void registerPrivateDnsSettingsCallbacks() { + for (Uri u : DnsManager.getPrivateDnsSettingsUris()) { + mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED); + } + } + private synchronized int nextNetworkRequestId() { return mNextNetworkRequestId++; } @@ -1490,15 +1502,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() { + private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() { @Override public void onUidRulesChanged(int uid, int uidRules) { // TODO: notify UID when it has requested targeted updates } @Override - public void onMeteredIfacesChanged(String[] meteredIfaces) { - } - @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { // TODO: relocate this specific callback in Tethering. if (restrictBackground) { @@ -1506,9 +1515,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mTethering.untetherAll(); } } - @Override - public void onUidPoliciesChanged(int uid, int uidPolicies) { - } }; /** @@ -2114,36 +2120,59 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mNetworkForNetId) { nai = mNetworkForNetId.get(msg.arg2); } - if (nai != null) { - final boolean valid = - (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); - final boolean wasValidated = nai.lastValidated; - final boolean wasDefault = isDefaultNetwork(nai); - if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") + - (msg.obj == null ? "" : " with redirect to " + (String)msg.obj)); - if (valid != nai.lastValidated) { - if (wasDefault) { - metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity( - SystemClock.elapsedRealtime(), valid); - } - final int oldScore = nai.getCurrentScore(); - nai.lastValidated = valid; - nai.everValidated |= valid; - updateCapabilities(oldScore, nai, nai.networkCapabilities); - // If score has changed, rebroadcast to NetworkFactories. b/17726566 - if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); + if (nai == null) break; + + final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); + final boolean wasValidated = nai.lastValidated; + final boolean wasDefault = isDefaultNetwork(nai); + + final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig) + ? (PrivateDnsConfig) msg.obj : null; + final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : ""; + + final boolean reevaluationRequired; + final String logMsg; + if (valid) { + reevaluationRequired = updatePrivateDns(nai, privateDnsCfg); + logMsg = (DBG && (privateDnsCfg != null)) + ? " with " + privateDnsCfg.toString() : ""; + } else { + reevaluationRequired = false; + logMsg = (DBG && !TextUtils.isEmpty(redirectUrl)) + ? " with redirect to " + redirectUrl : ""; + } + if (DBG) { + log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg); + } + // If there is a change in Private DNS configuration, + // trigger reevaluation of the network to test it. + if (reevaluationRequired) { + nai.networkMonitor.sendMessage( + NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID); + break; + } + if (valid != nai.lastValidated) { + if (wasDefault) { + metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity( + SystemClock.elapsedRealtime(), valid); } - updateInetCondition(nai); - // Let the NetworkAgent know the state of its network - Bundle redirectUrlBundle = new Bundle(); - redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, (String)msg.obj); - nai.asyncChannel.sendMessage( - NetworkAgent.CMD_REPORT_NETWORK_STATUS, - (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), - 0, redirectUrlBundle); - if (wasValidated && !nai.lastValidated) { - handleNetworkUnvalidated(nai); - } + final int oldScore = nai.getCurrentScore(); + nai.lastValidated = valid; + nai.everValidated |= valid; + updateCapabilities(oldScore, nai, nai.networkCapabilities); + // If score has changed, rebroadcast to NetworkFactories. b/17726566 + if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); + } + updateInetCondition(nai); + // Let the NetworkAgent know the state of its network + Bundle redirectUrlBundle = new Bundle(); + redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl); + nai.asyncChannel.sendMessage( + NetworkAgent.CMD_REPORT_NETWORK_STATUS, + (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), + 0, redirectUrlBundle); + if (wasValidated && !nai.lastValidated) { + handleNetworkUnvalidated(nai); } break; } @@ -2183,6 +2212,21 @@ public class ConnectivityService extends IConnectivityManager.Stub } break; } + case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: { + final NetworkAgentInfo nai; + synchronized (mNetworkForNetId) { + nai = mNetworkForNetId.get(msg.arg2); + } + if (nai == null) break; + + final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj; + final boolean reevaluationRequired = updatePrivateDns(nai, cfg); + if (nai.lastValidated && reevaluationRequired) { + nai.networkMonitor.sendMessage( + NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID); + } + break; + } } return true; } @@ -2218,6 +2262,63 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void handlePrivateDnsSettingsChanged() { + final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig(); + + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + // Private DNS only ever applies to networks that might provide + // Internet access and therefore also require validation. + if (!NetworkMonitor.isValidationRequired( + mDefaultRequest.networkCapabilities, nai.networkCapabilities)) { + continue; + } + + // Notify the NetworkMonitor thread in case it needs to cancel or + // schedule DNS resolutions. If a DNS resolution is required the + // result will be sent back to us. + nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg); + + if (!cfg.inStrictMode()) { + // No strict mode hostname DNS resolution needed, so just update + // DNS settings directly. In opportunistic and "off" modes this + // just reprograms netd with the network-supplied DNS servers + // (and of course the boolean of whether or not to attempt TLS). + // + // TODO: Consider code flow parity with strict mode, i.e. having + // NetworkMonitor relay the PrivateDnsConfig back to us and then + // performing this call at that time. + updatePrivateDns(nai, cfg); + } + } + } + + private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) { + final boolean reevaluationRequired = true; + final boolean dontReevaluate = false; + + final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg); + updateDnses(nai.linkProperties, null, nai.network.netId); + + if (newCfg == null) { + if (oldCfg == null) return dontReevaluate; + return oldCfg.useTls ? reevaluationRequired : dontReevaluate; + } + + if (oldCfg == null) { + return newCfg.useTls ? reevaluationRequired : dontReevaluate; + } + + if (oldCfg.useTls != newCfg.useTls) { + return reevaluationRequired; + } + + if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) { + return reevaluationRequired; + } + + return dontReevaluate; + } + private void updateLingerState(NetworkAgentInfo nai, long now) { // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm. // 2. If the network was lingering and there are now requests, unlinger it. @@ -2352,6 +2453,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } catch (Exception e) { loge("Exception removing network: " + e); } + mDnsManager.removeNetwork(nai.network); } synchronized (mNetworkForNetId) { mNetIdInUse.delete(nai.network.netId); @@ -2898,6 +3000,9 @@ public class ConnectivityService extends IConnectivityManager.Stub handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2)); break; } + case EVENT_PRIVATE_DNS_SETTINGS_CHANGED: + handlePrivateDnsSettingsChanged(); + break; } } } @@ -4587,11 +4692,12 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo defaultNai = getDefaultNetwork(); final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId); - Collection<InetAddress> dnses = newLp.getDnsServers(); - if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses); + if (DBG) { + final Collection<InetAddress> dnses = newLp.getDnsServers(); + log("Setting DNS servers for network " + netId + " to " + dnses); + } try { - mDnsManager.setDnsConfigurationForNetwork( - netId, dnses, newLp.getDomains(), isDefaultNetwork); + mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork); } catch (Exception e) { loge("Exception in setDnsConfigurationForNetwork: " + e); } @@ -4879,10 +4985,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } catch (Exception e) { loge("Exception setting default network :" + e); } + notifyLockdownVpn(newNetwork); handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy()); updateTcpBufferSizes(newNetwork); mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers()); + notifyIfacesChangedForNetworkStats(); } private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) { @@ -5456,12 +5564,30 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Returns the list of all interfaces that could be used by network traffic that does not + * explicitly specify a network. This includes the default network, but also all VPNs that are + * currently connected. + * + * Must be called on the handler thread. + */ + private Network[] getDefaultNetworks() { + ArrayList<Network> defaultNetworks = new ArrayList<>(); + NetworkAgentInfo defaultNetwork = getDefaultNetwork(); + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) { + defaultNetworks.add(nai.network); + } + } + return defaultNetworks.toArray(new Network[0]); + } + + /** * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the * properties tracked by NetworkStatsService on an active iface has changed. */ private void notifyIfacesChangedForNetworkStats() { try { - mStatsService.forceUpdateIfaces(); + mStatsService.forceUpdateIfaces(getDefaultNetworks()); } catch (Exception ignored) { } } @@ -5493,7 +5619,7 @@ public class ConnectivityService extends IConnectivityManager.Stub success = mVpns.get(user).setUnderlyingNetworks(networks); } if (success) { - notifyIfacesChangedForNetworkStats(); + mHandler.post(() -> notifyIfacesChangedForNetworkStats()); } return success; } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 985f16d910bc..a12c85aef85e 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1591,6 +1591,8 @@ public class DeviceIdleController extends SystemService mPowerSaveWhitelistExceptIdleAppIdArray = buildAppIdArray( mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds); + + passWhiteListToForceAppStandbyTrackerLocked(); } return true; } catch (PackageManager.NameNotFoundException e) { @@ -1608,6 +1610,8 @@ public class DeviceIdleController extends SystemService mPowerSaveWhitelistAppsExceptIdle, mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds); mPowerSaveWhitelistUserAppsExceptIdle.clear(); + + passWhiteListToForceAppStandbyTrackerLocked(); } } } @@ -2572,7 +2576,7 @@ public class DeviceIdleController extends SystemService private void passWhiteListToForceAppStandbyTrackerLocked() { ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds( - mPowerSaveWhitelistAllAppIdArray, + mPowerSaveWhitelistExceptIdleAppIdArray, mTempWhitelistAppIdArray); } diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java index 2d2c6b0bc8b3..e884de00c15a 100644 --- a/services/core/java/com/android/server/DiskStatsService.java +++ b/services/core/java/com/android/server/DiskStatsService.java @@ -19,6 +19,10 @@ package com.android.server; import android.content.Context; import android.os.Binder; import android.os.Environment; +import android.os.IBinder; +import android.os.IStoraged; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.StatFs; import android.os.SystemClock; import android.os.storage.StorageManager; @@ -109,6 +113,12 @@ public class DiskStatsService extends Binder { } } + if (protoFormat) { + reportDiskWriteSpeedProto(proto); + } else { + reportDiskWriteSpeed(pw); + } + reportFreeSpace(Environment.getDataDirectory(), "Data", pw, proto, DiskStatsFreeSpaceProto.FOLDER_DATA); reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw, proto, @@ -285,4 +295,41 @@ public class DiskStatsService extends Binder { Log.w(TAG, "exception reading diskstats cache file", e); } } + + private int getRecentPerf() throws RemoteException, IllegalStateException { + IBinder binder = ServiceManager.getService("storaged"); + if (binder == null) throw new IllegalStateException("storaged not found"); + IStoraged storaged = IStoraged.Stub.asInterface(binder); + return storaged.getRecentPerf(); + } + + // Keep reportDiskWriteSpeed and reportDiskWriteSpeedProto in sync + private void reportDiskWriteSpeed(PrintWriter pw) { + try { + long perf = getRecentPerf(); + if (perf != 0) { + pw.print("Recent Disk Write Speed (kB/s) = "); + pw.println(perf); + } else { + pw.println("Recent Disk Write Speed data unavailable"); + Log.w(TAG, "Recent Disk Write Speed data unavailable!"); + } + } catch (RemoteException | IllegalStateException e) { + pw.println(e.toString()); + Log.e(TAG, e.toString()); + } + } + + private void reportDiskWriteSpeedProto(ProtoOutputStream proto) { + try { + long perf = getRecentPerf(); + if (perf != 0) { + proto.write(DiskStatsServiceDumpProto.BENCHMARKED_WRITE_SPEED_KBPS, perf); + } else { + Log.w(TAG, "Recent Disk Write Speed data unavailable!"); + } + } catch (RemoteException | IllegalStateException e) { + Log.e(TAG, e.toString()); + } + } } diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java index 782d4ddab12a..de113a6a53e0 100644 --- a/services/core/java/com/android/server/ForceAppStandbyTracker.java +++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java @@ -103,6 +103,9 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); + /** + * System except-idle + user whitelist in the device idle controller. + */ @GuardedBy("mLock") private int[] mPowerWhitelistedAllAppIds = new int[0]; @@ -115,8 +118,11 @@ public class ForceAppStandbyTracker { @GuardedBy("mLock") boolean mStarted; + /** + * Only used for small battery use-case. + */ @GuardedBy("mLock") - boolean mIsCharging; + boolean mIsPluggedIn; @GuardedBy("mLock") boolean mBatterySaverEnabled; @@ -174,8 +180,8 @@ public class ForceAppStandbyTracker { } mForcedAppStandbyEnabled = enabled; if (DEBUG) { - Slog.d(TAG, - "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled); + Slog.d(TAG,"Forced app standby feature flag changed: " + + mForcedAppStandbyEnabled); } } mHandler.notifyForcedAppStandbyFeatureFlagChanged(); @@ -433,11 +439,8 @@ public class ForceAppStandbyTracker { private void updateForceAllAppStandbyState() { synchronized (mLock) { - if (mIsCharging) { - toggleForceAllAppsStandbyLocked(false); - } else if (mForceAllAppStandbyForSmallBattery - && isSmallBatteryDevice()) { - toggleForceAllAppsStandbyLocked(true); + if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) { + toggleForceAllAppsStandbyLocked(!mIsPluggedIn); } else { toggleForceAllAppsStandbyLocked(mBatterySaverEnabled); } @@ -589,10 +592,8 @@ public class ForceAppStandbyTracker { mHandler.doUserRemoved(userId); } } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { - int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); synchronized (mLock) { - mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING - || status == BatteryManager.BATTERY_STATUS_FULL); + mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); } updateForceAllAppStandbyState(); } @@ -817,21 +818,23 @@ public class ForceAppStandbyTracker { * @return whether alarms should be restricted for a UID package-name. */ public boolean areAlarmsRestricted(int uid, @NonNull String packageName) { - return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false); + return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false, + /* exemptOnBatterySaver =*/ false); } /** * @return whether jobs should be restricted for a UID package-name. */ public boolean areJobsRestricted(int uid, @NonNull String packageName) { - return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true); + return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true, + /* exemptOnBatterySaver =*/ false); } /** * @return whether force-app-standby is effective for a UID package-name. */ private boolean isRestricted(int uid, @NonNull String packageName, - boolean useTempWhitelistToo) { + boolean useTempWhitelistToo, boolean exemptOnBatterySaver) { if (isInForeground(uid)) { return false; } @@ -845,12 +848,13 @@ public class ForceAppStandbyTracker { ArrayUtils.contains(mTempWhitelistedAppIds, appId)) { return false; } - - if (mForceAllAppsStandby) { + if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) { return true; } - - return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName); + if (exemptOnBatterySaver) { + return false; + } + return mForceAllAppsStandby; } } @@ -930,8 +934,8 @@ public class ForceAppStandbyTracker { pw.println(mForceAllAppStandbyForSmallBattery); pw.print(indent); - pw.print("Charging: "); - pw.println(mIsCharging); + pw.print("Plugged In: "); + pw.println(mIsPluggedIn); pw.print(indent); pw.print("Foreground uids: ["); @@ -976,7 +980,7 @@ public class ForceAppStandbyTracker { isSmallBatteryDevice()); proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY, mForceAllAppStandbyForSmallBattery); - proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsCharging); + proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn); for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 55046959e4dd..21137adce69a 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -51,6 +51,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.Manifest; +import android.annotation.AnyThread; import android.annotation.BinderThread; import android.annotation.ColorInt; import android.annotation.IntDef; @@ -110,6 +111,7 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -256,6 +258,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String ACTION_SHOW_INPUT_METHOD_PICKER = "com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER"; + /** + * Debug flag for overriding runtime {@link SystemProperties}. + */ + @AnyThread + private static final class DebugFlag { + private static final Object LOCK = new Object(); + private final String mKey; + @GuardedBy("LOCK") + private boolean mValue; + + public DebugFlag(String key) { + mKey = key; + refresh(); + } + + void refresh() { + synchronized (LOCK) { + mValue = SystemProperties.getBoolean(mKey, true); + } + } + + boolean value() { + synchronized (LOCK) { + return mValue; + } + } + } + + /** + * Debug flags that can be overridden using "adb shell setprop <key>" + * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties". + */ + private static final class DebugFlags { + static final DebugFlag FLAG_OPTIMIZE_START_INPUT = + new DebugFlag("debug.optimize_startinput"); + } + + final Context mContext; final Resources mRes; final Handler mHandler; @@ -2930,8 +2970,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (!didStart && attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, - controlFlags, startInputReason); + if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value() + || (controlFlags + & InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, + controlFlags, startInputReason); + } } } } finally { @@ -4703,6 +4747,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mService.handleShellCommandSetInputMethod(this); case "reset": return mService.handleShellCommandResetInputMethod(this); + case "refresh_debug_properties": + return refreshDebugProperties(); default: getOutPrintWriter().println("Unknown command: " + imeCommand); return ShellCommandResult.FAILURE; @@ -4713,6 +4759,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread + @ShellCommandResult + private int refreshDebugProperties() { + DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh(); + return ShellCommandResult.SUCCESS; + } + + @BinderThread @Override public void onHelp() { try (PrintWriter pw = getOutPrintWriter()) { diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 9d228c3d0a6b..af33ab08cb9a 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1105,10 +1105,10 @@ public class IpSecService extends IIpSecService.Stub { * receive data. */ @Override - public synchronized IpSecTransformResponse createTransportModeTransform( - IpSecConfig c, IBinder binder) throws RemoteException { + public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder) + throws RemoteException { checkIpSecConfig(c); - checkNotNull(binder, "Null Binder passed to createTransportModeTransform"); + checkNotNull(binder, "Null Binder passed to createTransform"); final int resourceId = mNextResourceId++; UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); @@ -1186,7 +1186,7 @@ public class IpSecService extends IIpSecService.Stub { * other reasons. */ @Override - public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException { + public synchronized void deleteTransform(int resourceId) throws RemoteException { UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); releaseResource(userRecord.mTransformRecords, resourceId); } @@ -1235,8 +1235,8 @@ public class IpSecService extends IIpSecService.Stub { * reserved for future improved input validation. */ @Override - public synchronized void removeTransportModeTransforms( - ParcelFileDescriptor socket, int resourceId) throws RemoteException { + public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket) + throws RemoteException { try { mSrvConfig .getNetdInstance() diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index ea748db159e1..6c63f43234ab 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -232,10 +232,9 @@ public class LocationManagerService extends ILocationManager.Stub { private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>(); - private final ArrayMap<IGnssMeasurementsListener, Identity> mGnssMeasurementsListeners = - new ArrayMap<>(); + private final ArrayMap<IBinder, Identity> mGnssMeasurementsListeners = new ArrayMap<>(); - private final ArrayMap<IGnssNavigationMessageListener, Identity> + private final ArrayMap<IBinder, Identity> mGnssNavigationMessageListeners = new ArrayMap<>(); // current active user on the device - other users are denied location data @@ -438,23 +437,23 @@ public class LocationManagerService extends ILocationManager.Stub { applyRequirementsLocked(provider); } - for (Entry<IGnssMeasurementsListener, Identity> entry - : mGnssMeasurementsListeners.entrySet()) { + for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) { if (entry.getValue().mUid == uid) { if (D) { Log.d(TAG, "gnss measurements listener from uid " + uid + " is now " + (foreground ? "foreground" : "background)")); } if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssMeasurementsProvider.addListener(entry.getKey()); + mGnssMeasurementsProvider.addListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); } else { - mGnssMeasurementsProvider.removeListener(entry.getKey()); + mGnssMeasurementsProvider.removeListener( + IGnssMeasurementsListener.Stub.asInterface(entry.getKey())); } } } - for (Entry<IGnssNavigationMessageListener, Identity> entry - : mGnssNavigationMessageListeners.entrySet()) { + for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) { if (entry.getValue().mUid == uid) { if (D) { Log.d(TAG, "gnss navigation message listener from uid " @@ -462,9 +461,11 @@ public class LocationManagerService extends ILocationManager.Stub { + (foreground ? "foreground" : "background)")); } if (foreground || isThrottlingExemptLocked(entry.getValue())) { - mGnssNavigationMessageProvider.addListener(entry.getKey()); + mGnssNavigationMessageProvider.addListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); } else { - mGnssNavigationMessageProvider.removeListener(entry.getKey()); + mGnssNavigationMessageProvider.removeListener( + IGnssNavigationMessageListener.Stub.asInterface(entry.getKey())); } } } @@ -2401,7 +2402,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Identity callerIdentity = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - mGnssMeasurementsListeners.put(listener, callerIdentity); + mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) @@ -2421,7 +2422,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { if (mGnssMeasurementsProvider != null) { synchronized (mLock) { - mGnssMeasurementsListeners.remove(listener); + mGnssMeasurementsListeners.remove(listener.asBinder()); mGnssMeasurementsProvider.removeListener(listener); } } @@ -2438,7 +2439,7 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Identity callerIdentity = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - mGnssNavigationMessageListeners.put(listener, callerIdentity); + mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) @@ -2458,7 +2459,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { if (mGnssNavigationMessageProvider != null) { synchronized (mLock) { - mGnssNavigationMessageListeners.remove(listener); + mGnssNavigationMessageListeners.remove(listener.asBinder()); mGnssNavigationMessageProvider.removeListener(listener); } } @@ -3180,6 +3181,16 @@ public class LocationManagerService extends ILocationManager.Stub { pw.println(" " + record); } } + pw.println(" Active GnssMeasurement Listeners:"); + for (Identity identity : mGnssMeasurementsListeners.values()) { + pw.println(" " + identity.mPid + " " + identity.mUid + " " + + identity.mPackageName + ": " + isThrottlingExemptLocked(identity)); + } + pw.println(" Active GnssNavigationMessage Listeners:"); + for (Identity identity : mGnssNavigationMessageListeners.values()) { + pw.println(" " + identity.mPid + " " + identity.mUid + " " + + identity.mPackageName + ": " + isThrottlingExemptLocked(identity)); + } pw.println(" Overlay Provider Packages:"); for (LocationProviderInterface provider : mProviders) { if (provider instanceof LocationProviderProxy) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 8f646e75862c..88ae22477a9d 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -21,9 +21,6 @@ import static android.Manifest.permission.DUMP; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; -import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE; -import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; -import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE; @@ -1957,15 +1954,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - private static boolean shouldUseTls(ContentResolver cr) { - String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE); - if (TextUtils.isEmpty(privateDns)) { - privateDns = PRIVATE_DNS_DEFAULT_MODE; - } - return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) || - privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); - } - @Override public void addVpnUidRanges(int netId, UidRange[] ranges) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java index 80f8e519beb7..833def3e87b1 100644 --- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java +++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java @@ -26,4 +26,7 @@ public interface PersistentDataBlockManagerInternal { /** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */ byte[] getFrpCredentialHandle(); + + /** Update the OEM unlock enabled bit, bypassing user restriction checks. */ + void forceOemUnlockEnabled(boolean enabled); } diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index c32a2d10b0bc..4298140d31a0 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -668,5 +668,13 @@ public class PersistentDataBlockService extends SystemService { IoUtils.closeQuietly(inputStream); } } + + @Override + public void forceOemUnlockEnabled(boolean enabled) { + synchronized (mLock) { + doSetOemUnlockEnabledLocked(enabled); + computeAndWriteDigestLocked(); + } + } }; } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 35f83e41071c..30432df418ed 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -84,6 +84,7 @@ public class Watchdog extends Thread { "/system/bin/sdcard", "/system/bin/surfaceflinger", "media.extractor", // system/bin/mediaextractor + "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "com.android.bluetooth", // Bluetooth service }; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8cff20ce73c9..c0c684c41bc5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.Manifest.permission.BIND_VOICE_INTERACTION; import static android.Manifest.permission.CHANGE_CONFIGURATION; import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; +import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; @@ -192,11 +193,11 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_NONE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -293,6 +294,7 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; +import android.hardware.display.DisplayManagerInternal; import android.location.LocationManager; import android.media.audiofx.AudioEffect; import android.metrics.LogMaker; @@ -370,6 +372,7 @@ import android.util.Xml; import android.util.proto.ProtoOutputStream; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.RemoteAnimationDefinition; import android.view.View; import android.view.WindowManager; @@ -472,6 +475,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -8597,6 +8601,16 @@ public class ActivityManagerService extends IActivityManager.Stub } return false; } + + @Override + public int getPackageUid(String packageName, int flags) { + try { + return mActivityManagerService.mContext.getPackageManager() + .getPackageUid(packageName, flags); + } catch (NameNotFoundException nnfe) { + return -1; + } + } } class IntentFirewallInterface implements IntentFirewall.AMSInterface { @@ -25130,6 +25144,21 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean canStartMoreUsers() { return mUserController.canStartMoreUsers(); } + + @Override + public void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) { + mUserController.setSwitchingFromSystemUserMessage(switchingFromSystemUserMessage); + } + + @Override + public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) { + mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage); + } + + @Override + public int getMaxRunningUsers() { + return mUserController.mMaxRunningUsers; + } } /** @@ -25340,9 +25369,18 @@ public class ActivityManagerService extends IActivityManager.Stub } } } - if (updateFrameworkRes && mWindowManager != null) { - ActivityThread.currentActivityThread().getExecutor().execute( - mWindowManager::onOverlayChanged); + if (updateFrameworkRes) { + // Update system server components that need to know about changed overlays. Because the + // overlay is applied in ActivityThread, we need to serialize through its thread too. + final Executor executor = ActivityThread.currentActivityThread().getExecutor(); + final DisplayManagerInternal display = + LocalServices.getService(DisplayManagerInternal.class); + if (display != null) { + executor.execute(display::onOverlayChanged); + } + if (mWindowManager != null) { + executor.execute(mWindowManager::onOverlayChanged); + } } } @@ -25434,4 +25472,23 @@ public class ActivityManagerService extends IActivityManager.Stub } } } + + @Override + public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) + throws RemoteException { + enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "registerRemoteAnimations"); + synchronized (this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + final long origId = Binder.clearCallingIdentity(); + try { + r.registerRemoteAnimations(definition); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index ceec97c8f303..3bef87794c24 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -171,6 +171,7 @@ import android.util.proto.ProtoOutputStream; import android.view.AppTransitionAnimationSpec; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; +import android.view.RemoteAnimationDefinition; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -373,6 +374,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } + String getLifecycleDescription(String reason) { + return "packageName=" + packageName + ", state=" + state + ", reason=" + reason; + } + void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); pw.print(prefix); pw.print("packageName="); pw.print(packageName); @@ -1616,12 +1621,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // If the activity is stopped or stopping, cycle to the paused state. if (state == STOPPED || state == STOPPING) { + // Capture reason before state change + final String reason = getLifecycleDescription("makeVisibleIfNeeded"); + // An activity must be in the {@link PAUSING} state for the system to validate // the move to {@link PAUSED}. state = PAUSING; service.mLifecycleManager.scheduleTransaction(app.thread, appToken, PauseActivityItem.obtain(finishing, false /* userLeaving */, - configChangeFlags, false /* dontReport */)); + configChangeFlags, false /* dontReport */) + .setDescription(reason)); } } catch (Exception e) { // Just skip on any failure; we'll make it visible when it next restarts. @@ -2782,6 +2791,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return mStackSupervisor.topRunningActivityLocked() == this; } + void registerRemoteAnimations(RemoteAnimationDefinition definition) { + mWindowContainerController.registerRemoteAnimations(definition); + } + @Override public String toString() { if (stringName != null) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index a343965ab848..9d06b0dbab64 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -84,14 +84,14 @@ import static com.android.server.am.proto.ActivityStackProto.FULLSCREEN; import static com.android.server.am.proto.ActivityStackProto.ID; import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY; import static com.android.server.am.proto.ActivityStackProto.TASKS; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_NONE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; import static java.lang.Integer.MAX_VALUE; @@ -606,7 +606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai true /* onTop */); recentStack.moveToFront("setWindowingMode"); // If task moved to docked stack - show recents if needed. - mService.mWindowManager.showRecentApps(false /* fromHome */); + mService.mWindowManager.showRecentApps(); } wm.continueSurfaceLayout(); } @@ -1817,7 +1817,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean behindFullscreenActivity = !stackShouldBeVisible; boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this) && (isInStackLocked(starting) == null); - final boolean isTopNotPinnedStack = getDisplay().isTopNotPinnedStack(this); + final boolean isTopNotPinnedStack = + isAttached() && getDisplay().isTopNotPinnedStack(this); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -2639,7 +2640,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.clearOptionsLocked(); mService.mLifecycleManager.scheduleTransaction(next.app.thread, next.appToken, ResumeActivityItem.obtain(next.app.repProcState, - mService.isNextTransitionForward())); + mService.isNextTransitionForward()) + .setDescription(next.getLifecycleDescription( + "resumeTopActivityInnerLocked"))); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next); @@ -3419,7 +3422,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai EventLogTags.writeAmStopActivity( r.userId, System.identityHashCode(r), r.shortComponentName); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - StopActivityItem.obtain(r.visible, r.configChangeFlags)); + StopActivityItem.obtain(r.visible, r.configChangeFlags) + .setDescription(r.getLifecycleDescription("stopActivityLocked"))); if (shouldSleepOrShutDownActivities()) { r.setSleeping(true); } @@ -4225,7 +4229,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai try { if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r); mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken, - DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)); + DestroyActivityItem.obtain(r.finishing, r.configChangeFlags) + .setDescription(r.getLifecycleDescription("destroyActivityLocked"))); } catch (Exception e) { // We can just ignore exceptions here... if the process // has crashed, our death notification will clean things diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 16c2d2d68bc5..8168cba213f5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -94,7 +94,7 @@ import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS; import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID; import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY; -import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; import static java.lang.Integer.MAX_VALUE; @@ -1424,7 +1424,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()) + .setDescription(r.getLifecycleDescription("realStartActivityLocked")); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 35465a7933fc..26d65bc7414c 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -738,9 +738,18 @@ class AppErrors { } return; } + final boolean showFirstCrash = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0; + final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, + 0, + mService.mUserController.getCurrentUserId()) != 0; final boolean crashSilenced = mAppsNotReportingCrashes != null && mAppsNotReportingCrashes.contains(proc.info.packageName); - if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced) { + if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced + && (showFirstCrash || showFirstCrashDevOption || data.repeating)) { proc.crashDialog = new AppErrorDialog(mContext, mService, data); } else { // The device is asleep, so just pretend that the user diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 81e8eb0d4136..207aaa76e5b8 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -40,6 +40,7 @@ import android.os.UserManagerInternal; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.os.connectivity.CellularBatteryStats; +import android.os.connectivity.GpsBatteryStats; import android.os.health.HealthStatsParceler; import android.os.health.HealthStatsWriter; import android.os.health.UidHealthStats; @@ -594,6 +595,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + public void noteGpsSignalQuality(int signalLevel) { + synchronized (mStats) { + mStats.noteGpsSignalQualityLocked(signalLevel); + } + } + public void noteScreenState(int state) { enforceCallingPermission(); if (DBG) Slog.d(TAG, "begin noteScreenState"); @@ -1448,6 +1455,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } /** + * Gets a snapshot of Gps stats + * @hide + */ + public GpsBatteryStats getGpsBatteryStats() { + synchronized (mStats) { + return mStats.getGpsBatteryStats(); + } + } + + /** * Gets a snapshot of the system health for a particular uid. */ @Override diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java index c9afc17e1eb4..d6c6f96285a6 100644 --- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java @@ -23,6 +23,7 @@ import android.os.SystemProperties; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import android.view.ThreadedRenderer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; @@ -39,6 +40,7 @@ class GlobalSettingsToPropertiesMapper { // List mapping entries in the following format: // {Settings.Global.SETTING_NAME, "system_property_name"}, {Settings.Global.SYS_VDSO, "sys.vdso"}, + {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR}, }; diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java index 4d7bc1e4af56..79f3fe3ffd9f 100644 --- a/services/core/java/com/android/server/am/KeyguardController.java +++ b/services/core/java/com/android/server/am/KeyguardController.java @@ -27,13 +27,13 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED; import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE; -import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_UNSET; import android.app.ActivityManagerInternal.SleepToken; import android.os.IBinder; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index b131e86d83ed..809f19f64317 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -749,12 +749,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(), DEFAULT_DISPLAY, toStack); - boolean successful = (preferredStack == toStack); - if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // If task moved to docked stack - show recents if needed. - mService.mWindowManager.showRecentApps(false /* fromHome */); - } - return successful; + return (preferredStack == toStack); } /** diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index a327a018bc25..1a47aa5cd777 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -217,6 +217,18 @@ class UserController implements Handler.Callback { private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks; /** + * Messages for for switching from {@link android.os.UserHandle#SYSTEM}. + */ + @GuardedBy("mLock") + private String mSwitchingFromSystemUserMessage; + + /** + * Messages for for switching to {@link android.os.UserHandle#SYSTEM}. + */ + @GuardedBy("mLock") + private String mSwitchingToSystemUserMessage; + + /** * Callbacks that are still active after {@link #USER_SWITCH_TIMEOUT_MS} */ @GuardedBy("mLock") @@ -783,11 +795,13 @@ class UserController implements Handler.Callback { */ private void stopGuestOrEphemeralUserIfBackground(int oldUserId) { if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId); - UserState oldUss = mStartedUsers.get(oldUserId); - if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId - || oldUss.state == UserState.STATE_STOPPING - || oldUss.state == UserState.STATE_SHUTDOWN) { - return; + synchronized(mLock) { + UserState oldUss = mStartedUsers.get(oldUserId); + if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null + || oldUss.state == UserState.STATE_STOPPING + || oldUss.state == UserState.STATE_SHUTDOWN) { + return; + } } UserInfo userInfo = getUserInfo(oldUserId); @@ -1189,7 +1203,8 @@ class UserController implements Handler.Callback { private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) { // The dialog will show and then initiate the user switch by calling startUserInForeground - mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second); + mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second, + getSwitchingFromSystemUserMessage(), getSwitchingToSystemUserMessage()); } private void dispatchForegroundProfileChanged(int userId) { @@ -1799,6 +1814,30 @@ class UserController implements Handler.Callback { return mLockPatternUtils.isLockScreenDisabled(userId); } + void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) { + synchronized (mLock) { + mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage; + } + } + + void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) { + synchronized (mLock) { + mSwitchingToSystemUserMessage = switchingToSystemUserMessage; + } + } + + private String getSwitchingFromSystemUserMessage() { + synchronized (mLock) { + return mSwitchingFromSystemUserMessage; + } + } + + private String getSwitchingToSystemUserMessage() { + synchronized (mLock) { + return mSwitchingToSystemUserMessage; + } + } + void dump(PrintWriter pw, boolean dumpAll) { synchronized (mLock) { pw.println(" mStartedUsers:"); @@ -2079,9 +2118,11 @@ class UserController implements Handler.Callback { mService.installEncryptionUnawareProviders(userId); } - void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser) { + void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser, + String switchingFromSystemUserMessage, String switchingToSystemUserMessage) { Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser, - true /* above system */); + true /* above system */, switchingFromSystemUserMessage, + switchingToSystemUserMessage); d.show(); } diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 3e6934f67e4e..afcba3bfe330 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -53,7 +53,8 @@ final class UserSwitchingDialog extends AlertDialog private boolean mStartedUser; public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser, - UserInfo newUser, boolean aboveSystem) { + UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage, + String switchingToSystemUserMessage) { super(context); mService = service; @@ -65,7 +66,7 @@ final class UserSwitchingDialog extends AlertDialog // Custom view due to alignment and font size requirements View view = LayoutInflater.from(getContext()).inflate(R.layout.user_switching_dialog, null); - String viewMessage; + String viewMessage = null; if (UserManager.isSplitSystemUser() && newUser.id == UserHandle.USER_SYSTEM) { viewMessage = res.getString(R.string.user_logging_out_message, oldUser.name); } else if (UserManager.isDeviceInDemoMode(context)) { @@ -75,7 +76,17 @@ final class UserSwitchingDialog extends AlertDialog viewMessage = res.getString(R.string.demo_starting_message); } } else { - viewMessage = res.getString(R.string.user_switching_message, newUser.name); + if (oldUser.id == UserHandle.USER_SYSTEM) { + viewMessage = switchingFromSystemUserMessage; + } else if (newUser.id == UserHandle.USER_SYSTEM) { + viewMessage = switchingToSystemUserMessage; + } + + // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null, fallback + // to system message. + if (viewMessage == null) { + viewMessage = res.getString(R.string.user_switching_message, newUser.name); + } } ((TextView) view.findViewById(R.id.message)).setText(viewMessage); setView(view); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 799f2a92bc33..a7147206bda2 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1047,9 +1047,11 @@ public class AudioService extends IAudioService.Stub private void checkMuteAffectedStreams() { // any stream with a min level > 0 is not muteable by definition + // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission. for (int i = 0; i < mStreamStates.length; i++) { final VolumeStreamState vss = mStreamStates[i]; - if (vss.mIndexMin > 0) { + if (vss.mIndexMin > 0 && + vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) { mMuteAffectedStreams &= ~(1 << vss.mStreamType); } } @@ -1412,6 +1414,18 @@ public class AudioService extends IAudioService.Stub return; } + // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure + // that the calling app have the MODIFY_PHONE_STATE permission. + if (isMuteAdjust && + streamType == AudioSystem.STREAM_VOICE_CALL && + mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + // use stream type alias here so that streams with same alias have the same behavior, // including with regard to silent mode control (e.g the use of STREAM_RING below and in // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) @@ -1712,6 +1726,15 @@ public class AudioService extends IAudioService.Stub + " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage); return; } + if ((streamType == AudioManager.STREAM_VOICE_CALL) && + (index == 0) && + (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED)) { + Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without" + + " MODIFY_PHONE_STATE callingPackage=" + callingPackage); + return; + } mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType, index/*val1*/, flags/*val2*/, callingPackage)); setStreamVolume(streamType, index, flags, callingPackage, callingPackage, @@ -4132,22 +4155,30 @@ public class AudioService extends IAudioService.Stub public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile) { + return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + device, state, profile, false /* suppressNoisyIntent */); + } + + public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device, + int state, int profile, boolean suppressNoisyIntent) + { if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) { return 0; } return setBluetoothA2dpDeviceConnectionStateInt( - device, state, profile, AudioSystem.DEVICE_NONE); + device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE); } public int setBluetoothA2dpDeviceConnectionStateInt( - BluetoothDevice device, int state, int profile, int musicDevice) + BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, + int musicDevice) { int delay; if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { throw new IllegalArgumentException("invalid profile " + profile); } synchronized (mConnectedDevices) { - if (profile == BluetoothProfile.A2DP) { + if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState, musicDevice); @@ -4503,27 +4534,30 @@ public class AudioService extends IAudioService.Stub if (mStreamType == srcStream.mStreamType) { return; } - synchronized (VolumeStreamState.class) { - int srcStreamType = srcStream.getStreamType(); - // apply default device volume from source stream to all devices first in case - // some devices are present in this stream state but not in source stream state - int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); - index = rescaleIndex(index, srcStreamType, mStreamType); - for (int i = 0; i < mIndexMap.size(); i++) { - mIndexMap.put(mIndexMap.keyAt(i), index); - } - // Now apply actual volume for devices in source stream state - SparseIntArray srcMap = srcStream.mIndexMap; - for (int i = 0; i < srcMap.size(); i++) { - int device = srcMap.keyAt(i); - index = srcMap.valueAt(i); + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + int srcStreamType = srcStream.getStreamType(); + // apply default device volume from source stream to all devices first in case + // some devices are present in this stream state but not in source stream state + int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); index = rescaleIndex(index, srcStreamType, mStreamType); - - setIndex(index, device, caller); + for (int i = 0; i < mIndexMap.size(); i++) { + mIndexMap.put(mIndexMap.keyAt(i), index); + } + // Now apply actual volume for devices in source stream state + SparseIntArray srcMap = srcStream.mIndexMap; + for (int i = 0; i < srcMap.size(); i++) { + int device = srcMap.keyAt(i); + index = srcMap.valueAt(i); + index = rescaleIndex(index, srcStreamType, mStreamType); + + setIndex(index, device, caller); + } } } } + @GuardedBy("mSettingsLock") public void setAllIndexesToMax() { synchronized (VolumeStreamState.class) { for (int i = 0; i < mIndexMap.size(); i++) { @@ -5397,7 +5431,7 @@ public class AudioService extends IAudioService.Stub // consistent with audio policy manager state setBluetoothA2dpDeviceConnectionStateInt( btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP, - musicDevice); + false /* suppressNoisyIntent */, musicDevice); } } } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index e093c9df7c4b..1ae7d20fb3fe 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -222,7 +222,7 @@ class TunerSession extends ITuner.Stub { MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); MutableBoolean flagState = new MutableBoolean(false); try { - mHwSession.getConfigFlag(flag, (int result, boolean value) -> { + mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> { halResult.value = result; flagState.value = value; }); diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index a4170cede361..a1c54bd4885b 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE; +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.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES; @@ -29,19 +30,32 @@ import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.net.LinkProperties; +import android.net.Network; import android.net.NetworkUtils; +import android.net.Uri; import android.os.Binder; import android.os.INetworkManagementService; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.system.GaiException; +import android.system.OsConstants; +import android.system.StructAddrinfo; import android.text.TextUtils; import android.util.Slog; import com.android.server.connectivity.MockableSystemProperties; +import libcore.io.Libcore; + import java.net.InetAddress; +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.StringJoiner; /** @@ -61,10 +75,86 @@ public class DnsManager { private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; + public static class PrivateDnsConfig { + public final boolean useTls; + public final String hostname; + public final InetAddress[] ips; + + public PrivateDnsConfig() { + this(false); + } + + public PrivateDnsConfig(boolean useTls) { + this.useTls = useTls; + this.hostname = ""; + this.ips = new InetAddress[0]; + } + + public PrivateDnsConfig(String hostname, InetAddress[] ips) { + this.useTls = !TextUtils.isEmpty(hostname); + this.hostname = useTls ? hostname : ""; + this.ips = (ips != null) ? ips : new InetAddress[0]; + } + + public PrivateDnsConfig(PrivateDnsConfig cfg) { + useTls = cfg.useTls; + hostname = cfg.hostname; + ips = cfg.ips; + } + + public boolean inStrictMode() { + return useTls && !TextUtils.isEmpty(hostname); + } + + public String toString() { + return PrivateDnsConfig.class.getSimpleName() + + "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}"; + } + } + + public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { + final String mode = getPrivateDnsMode(cr); + + final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode); + + if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) { + final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER); + return new PrivateDnsConfig(specifier, null); + } + + return new PrivateDnsConfig(useTls); + } + + public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) { + final StructAddrinfo hints = new StructAddrinfo(); + // Unnecessary, but expressly no AI_ADDRCONFIG. + hints.ai_flags = 0; + // Fetch all IP addresses at once to minimize re-resolution. + hints.ai_family = OsConstants.AF_UNSPEC; + hints.ai_socktype = OsConstants.SOCK_DGRAM; + + try { + final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId); + if (ips != null && ips.length > 0) { + return new PrivateDnsConfig(name, ips); + } + } catch (GaiException ignored) {} + + return null; + } + + public static Uri[] getPrivateDnsSettingsUris() { + final Uri[] uris = new Uri[2]; + uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE); + uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER); + return uris; + } + private final Context mContext; private final ContentResolver mContentResolver; private final INetworkManagementService mNMS; private final MockableSystemProperties mSystemProperties; + private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap; private int mNumDnsEntries; private int mSampleValidity; @@ -79,44 +169,55 @@ public class DnsManager { mContentResolver = mContext.getContentResolver(); mNMS = nms; mSystemProperties = sp; + mPrivateDnsMap = new HashMap<>(); // TODO: Create and register ContentObservers to track every setting // used herein, posting messages to respond to changes. } - public boolean isPrivateDnsInStrictMode() { - return !TextUtils.isEmpty(mPrivateDnsMode) && - mPrivateDnsMode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) && - !TextUtils.isEmpty(mPrivateDnsSpecifier); + public PrivateDnsConfig getPrivateDnsConfig() { + return getPrivateDnsConfig(mContentResolver); + } + + public void removeNetwork(Network network) { + mPrivateDnsMap.remove(network.netId); + } + + public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) { + Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")"); + return (cfg != null) + ? mPrivateDnsMap.put(network.netId, cfg) + : mPrivateDnsMap.remove(network); } public void setDnsConfigurationForNetwork( - int netId, Collection<InetAddress> servers, String domains, boolean isDefaultNetwork) { - updateParametersSettings(); - updatePrivateDnsSettings(); + int netId, LinkProperties lp, boolean isDefaultNetwork) { + // We only use the PrivateDnsConfig data pushed to this class instance + // from ConnectivityService because it works in coordination with + // NetworkMonitor to decide which networks need validation and runs the + // blocking calls to resolve Private DNS strict mode hostnames. + // + // At this time we do attempt to enable Private DNS on non-Internet + // networks like IMS. + final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId); + + final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls; + final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode(); + final String tlsHostname = strictMode ? privateDnsCfg.hostname : ""; - final String[] serverStrs = NetworkUtils.makeStrings(servers); - final String[] domainStrs = (domains == null) ? new String[0] : domains.split(" "); + final String[] serverStrs = NetworkUtils.makeStrings( + strictMode ? Arrays.stream(privateDnsCfg.ips) + .filter((ip) -> lp.isReachable(ip)) + .collect(Collectors.toList()) + : lp.getDnsServers()); + final String[] domainStrs = getDomainStrings(lp.getDomains()); + + updateParametersSettings(); final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples }; - final boolean useTls = shouldUseTls(mPrivateDnsMode); - // TODO: Populate tlsHostname once it's decided how the hostname's IP - // addresses will be resolved: - // - // [1] network-provided DNS servers are included here with the - // hostname and netd will use the network-provided servers to - // resolve the hostname and fix up its internal structures, or - // - // [2] network-provided DNS servers are included here without the - // hostname, the ConnectivityService layer resolves the given - // hostname, and then reconfigures netd with this information. - // - // In practice, there will always be a need for ConnectivityService or - // the captive portal app to use the network-provided services to make - // some queries. This argues in favor of [1], in concert with another - // mechanism, perhaps setting a high bit in the netid, to indicate - // via existing DNS APIs which set of servers (network-provided or - // non-network-provided private DNS) should be queried. - final String tlsHostname = ""; + + Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)", + netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs), + Arrays.toString(params), useTls, tlsHostname)); try { mNMS.setDnsConfigurationForNetwork( netId, serverStrs, domainStrs, params, useTls, tlsHostname); @@ -129,7 +230,7 @@ public class DnsManager { // default network, and we should just set net.dns1 to ::1, not least // because applications attempting to use net.dns resolvers will bypass // the privacy protections of things like DNS-over-TLS. - if (isDefaultNetwork) setDefaultDnsSystemProperties(servers); + if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers()); flushVmDnsCache(); } @@ -163,11 +264,6 @@ public class DnsManager { } } - private void updatePrivateDnsSettings() { - mPrivateDnsMode = getStringSetting(PRIVATE_DNS_MODE); - mPrivateDnsSpecifier = getStringSetting(PRIVATE_DNS_SPECIFIER); - } - private void updateParametersSettings() { mSampleValidity = getIntSetting( DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, @@ -198,10 +294,6 @@ public class DnsManager { } } - private String getStringSetting(String which) { - return Settings.Global.getString(mContentResolver, which); - } - private int getIntSetting(String which, int dflt) { return Settings.Global.getInt(mContentResolver, which, dflt); } @@ -216,11 +308,16 @@ public class DnsManager { } } - private static boolean shouldUseTls(String mode) { - if (TextUtils.isEmpty(mode)) { - mode = PRIVATE_DNS_DEFAULT_MODE; - } - return mode.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) || - mode.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); + private static String getPrivateDnsMode(ContentResolver cr) { + final String mode = getStringSetting(cr, PRIVATE_DNS_MODE); + return !TextUtils.isEmpty(mode) ? mode : PRIVATE_DNS_DEFAULT_MODE; + } + + private static String getStringSetting(ContentResolver cr, String which) { + return Settings.Global.getString(cr, which); + } + + private static String[] getDomainStrings(String domains) { + return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" "); } } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 768403024353..ed268581b50c 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -29,6 +29,7 @@ import android.net.CaptivePortal; import android.net.ConnectivityManager; import android.net.ICaptivePortal; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.ProxyInfo; import android.net.TrafficStats; @@ -215,6 +216,15 @@ public class NetworkMonitor extends StateMachine { */ private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12; + /** + * ConnectivityService notifies NetworkMonitor of settings changes to + * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in + * strict mode, then an event is sent back to ConnectivityService with the + * result of the resolution attempt. + */ + private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13; + public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14; + // Start mReevaluateDelayMs at this value and double. private static final int INITIAL_REEVALUATE_DELAY_MS = 1000; private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000; @@ -230,6 +240,12 @@ public class NetworkMonitor extends StateMachine { private static final int NUM_VALIDATION_LOG_LINES = 20; + public static boolean isValidationRequired( + NetworkCapabilities dfltNetCap, NetworkCapabilities nc) { + // TODO: Consider requiring validation for DUN networks. + return dfltNetCap.satisfiedByNetworkCapabilities(nc); + } + private final Context mContext; private final Handler mConnectivityServiceHandler; private final NetworkAgentInfo mNetworkAgentInfo; @@ -261,6 +277,8 @@ public class NetworkMonitor extends StateMachine { public boolean systemReady = false; + private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null; + private final State mDefaultState = new DefaultState(); private final State mValidatedState = new ValidatedState(); private final State mMaybeNotifyState = new MaybeNotifyState(); @@ -342,6 +360,11 @@ public class NetworkMonitor extends StateMachine { return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION; } + private boolean isValidationRequired() { + return isValidationRequired( + mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities); + } + // DefaultState is the parent of all States. It exists only to handle CMD_* messages but // does not entail any real state (hence no enter() or exit() routines). private class DefaultState extends State { @@ -405,6 +428,18 @@ public class NetworkMonitor extends StateMachine { break; } return HANDLED; + case CMD_PRIVATE_DNS_SETTINGS_CHANGED: + if (isValidationRequired()) { + // This performs a blocking DNS resolution of the + // strict mode hostname, if required. + resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj); + if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) { + mConnectivityServiceHandler.sendMessage(obtainMessage( + EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, + new DnsManager.PrivateDnsConfig(mPrivateDnsCfg))); + } + } + return HANDLED; default: return HANDLED; } @@ -421,7 +456,7 @@ public class NetworkMonitor extends StateMachine { maybeLogEvaluationResult( networkEventType(validationStage(), EvaluationResult.VALIDATED)); mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, - NETWORK_TEST_RESULT_VALID, mNetId, null)); + NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg)); mValidations++; } @@ -567,9 +602,9 @@ public class NetworkMonitor extends StateMachine { // the network so don't bother validating here. Furthermore sending HTTP // packets over the network may be undesirable, for example an extremely // expensive metered network, or unwanted leaking of the User Agent string. - if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities( - mNetworkAgentInfo.networkCapabilities)) { + if (!isValidationRequired()) { validationLog("Network would not satisfy default request, not validating"); + mPrivateDnsCfg = null; transitionTo(mValidatedState); return HANDLED; } @@ -582,6 +617,7 @@ public class NetworkMonitor extends StateMachine { // if this is found to cause problems. CaptivePortalProbeResult probeResult = isCaptivePortal(); if (probeResult.isSuccessful()) { + resolvePrivateDnsConfig(); transitionTo(mValidatedState); } else if (probeResult.isPortal()) { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, @@ -1045,6 +1081,44 @@ public class NetworkMonitor extends StateMachine { return null; } + public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) { + // Cancel any outstanding resolutions. + removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED); + // Send the update to the proper thread. + sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg); + } + + private void resolvePrivateDnsConfig() { + resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver())); + } + + private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) { + // Nothing to do. + if (cfg == null) { + mPrivateDnsCfg = null; + return; + } + + // No DNS resolution required. + if (!cfg.inStrictMode()) { + mPrivateDnsCfg = cfg; + return; + } + + if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() && + (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) { + // We have already resolved this strict mode hostname. Assume that + // Private DNS services won't be changing serving IP addresses very + // frequently and save ourselves one re-resolve. + return; + } + + mPrivateDnsCfg = cfg; + final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf( + mNetwork, mPrivateDnsCfg.hostname); + if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg; + } + /** * @param responseReceived - whether or not we received a valid HTTP response to our request. * If false, isCaptivePortal and responseTimestampMs are ignored diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 3c2d72407b4e..aa174e3ad715 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -18,6 +18,7 @@ package com.android.server.connectivity; import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.net.ConnectivityManager.NETID_UNSET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.RouteInfo.RTN_THROW; @@ -298,11 +299,13 @@ public class Vpn { int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; boolean metered = false; boolean roaming = false; + boolean congested = false; if (ArrayUtils.isEmpty(underlyingNetworks)) { // No idea what the underlying networks are; assume sane defaults metered = true; roaming = false; + congested = false; } else { for (Network underlying : underlyingNetworks) { final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); @@ -319,22 +322,16 @@ public class Vpn { underlyingCaps.getLinkUpstreamBandwidthKbps()); metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED); roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING); + congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); } } caps.setTransportTypes(transportTypes); caps.setLinkDownstreamBandwidthKbps(downKbps); caps.setLinkUpstreamBandwidthKbps(upKbps); - if (metered) { - caps.removeCapability(NET_CAPABILITY_NOT_METERED); - } else { - caps.addCapability(NET_CAPABILITY_NOT_METERED); - } - if (roaming) { - caps.removeCapability(NET_CAPABILITY_NOT_ROAMING); - } else { - caps.addCapability(NET_CAPABILITY_NOT_ROAMING); - } + caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered); + caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming); + caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested); } /** diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 1e94e00a09a9..bcf8bfe6aaad 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -521,6 +521,7 @@ public class BrightnessTracker { public void dump(PrintWriter pw) { pw.println("BrightnessTracker state:"); synchronized (mDataCollectionLock) { + pw.println(" mStarted=" + mStarted); pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size()); if (!mLastSensorReadings.isEmpty()) { pw.println(" mLastSensorReadings time span " diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 839ab4d4867d..3a8e291f7976 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -143,6 +143,9 @@ abstract class DisplayDevice { public void requestDisplayModesInTransactionLocked(int colorMode, int modeId) { } + public void onOverlayChangedLocked() { + } + /** * Sets the display layer stack while in a transaction. */ diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a55fec5246d3..b97de6511aed 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -29,6 +29,7 @@ import com.android.internal.util.IndentingPrintWriter; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Context; @@ -1005,11 +1006,13 @@ public final class DisplayManagerService extends SystemService { } private void setBrightnessConfigurationForUserInternal( - @NonNull BrightnessConfiguration c, @UserIdInt int userId) { + @NonNull BrightnessConfiguration c, @UserIdInt int userId, + @Nullable String packageName) { final int userSerial = getUserManager().getUserSerialNumber(userId); synchronized (mSyncRoot) { try { - mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial); + mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial, + packageName); } finally { mPersistentDataStore.saveIfNeeded(); } @@ -1833,7 +1836,7 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public void setBrightnessConfigurationForUser( - BrightnessConfiguration c, @UserIdInt int userId) { + BrightnessConfiguration c, @UserIdInt int userId, String packageName) { mContext.enforceCallingOrSelfPermission( Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, "Permission required to change the display's brightness configuration"); @@ -1843,10 +1846,13 @@ public final class DisplayManagerService extends SystemService { "Permission required to change the display brightness" + " configuration of another user"); } + if (packageName != null && !validatePackageName(getCallingUid(), packageName)) { + packageName = null; + } Preconditions.checkNotNull(c); final long token = Binder.clearCallingIdentity(); try { - setBrightnessConfigurationForUserInternal(c, userId); + setBrightnessConfigurationForUserInternal(c, userId, packageName); } finally { Binder.restoreCallingIdentity(token); } @@ -2013,8 +2019,8 @@ public final class DisplayManagerService extends SystemService { @Override public void onOverlayChanged() { synchronized (mSyncRoot) { - if (updateLogicalDisplaysLocked()) { - scheduleTraversalLocked(false); + for (int i = 0; i < mDisplayDevices.size(); i++) { + mDisplayDevices.get(i).onOverlayChangedLocked(); } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 23e4c9bf69cd..b7385d85d296 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -404,7 +404,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) { mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND; } - mInfo.displayCutout = parseDefaultDisplayCutout(res); + mInfo.displayCutout = DisplayCutout.fromResources(res, mInfo.width); mInfo.type = Display.TYPE_BUILT_IN; mInfo.densityDpi = (int)(phys.density * 160 + 0.5f); mInfo.xDpi = phys.xDpi; @@ -440,15 +440,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { return mInfo; } - private DisplayCutout parseDefaultDisplayCutout(Resources res) { - String cutoutString = res.getString( - com.android.internal.R.string.config_mainBuiltInDisplayCutout); - if (TextUtils.isEmpty(cutoutString)) { - return null; - } - return DisplayCutout.fromBounds(PathParser.createPathFromPathData(cutoutString)); - } - @Override public Runnable requestDisplayStateLocked(final int state, final int brightness) { // Assume that the brightness is off if the display is being turned off. @@ -600,6 +591,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + @Override + public void onOverlayChangedLocked() { + updateDeviceInfoLocked(); + } + public boolean requestModeInTransactionLocked(int modeId) { if (modeId == 0) { modeId = mDefaultModeId; diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 49b4465efac7..f1ce5c5f0007 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.annotation.Nullable; import android.graphics.Point; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.WifiDisplay; @@ -30,6 +31,8 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.Pair; +import android.util.SparseLongArray; +import android.util.TimeUtils; import android.util.Xml; import android.view.Display; @@ -73,8 +76,8 @@ import libcore.util.Objects; * <stable-display-width>1080</stable-display-width> * </stable-device-values> * <brightness-configurations> - * <brightness-configuration user-id="0"> - * <brightness-curve> + * <brightness-configuration user-serial="0" package-name="com.example" timestamp="1234"> + * <brightness-curve description="some text"> * <brightness-point lux="0" nits="13.25"/> * <brightness-point lux="20" nits="35.94"/> * </brightness-curve> @@ -110,8 +113,11 @@ final class PersistentDataStore { private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve"; private static final String TAG_BRIGHTNESS_POINT = "brightness-point"; private static final String ATTR_USER_SERIAL = "user-serial"; + private static final String ATTR_PACKAGE_NAME = "package-name"; + private static final String ATTR_TIME_STAMP = "timestamp"; private static final String ATTR_LUX = "lux"; private static final String ATTR_NITS = "nits"; + private static final String ATTR_DESCRIPTION = "description"; // Remembered Wifi display devices. private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>(); @@ -273,9 +279,11 @@ final class PersistentDataStore { } } - public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial) { + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, + @Nullable String packageName) { loadIfNeeded(); - if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial)) { + if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial, + packageName)) { setDirty(); } } @@ -576,15 +584,27 @@ final class PersistentDataStore { private static final class BrightnessConfigurations { // Maps from a user ID to the users' given brightness configuration private SparseArray<BrightnessConfiguration> mConfigurations; + // Timestamp of time the configuration was set. + private SparseLongArray mTimeStamps; + // Package that set the configuration. + private SparseArray<String> mPackageNames; public BrightnessConfigurations() { mConfigurations = new SparseArray<>(); + mTimeStamps = new SparseLongArray(); + mPackageNames = new SparseArray<>(); } private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c, - int userSerial) { + int userSerial, String packageName) { BrightnessConfiguration currentConfig = mConfigurations.get(userSerial); if (currentConfig == null || !currentConfig.equals(c)) { + if (packageName == null) { + mPackageNames.remove(userSerial); + } else { + mPackageNames.put(userSerial, packageName); + } + mTimeStamps.put(userSerial, System.currentTimeMillis()); mConfigurations.put(userSerial, c); return true; } @@ -604,14 +624,31 @@ final class PersistentDataStore { userSerial = Integer.parseInt( parser.getAttributeValue(null, ATTR_USER_SERIAL)); } catch (NumberFormatException nfe) { - userSerial= -1; + userSerial = -1; Slog.e(TAG, "Failed to read in brightness configuration", nfe); } + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP); + long timeStamp = -1; + if (timeStampString != null) { + try { + timeStamp = Long.parseLong(timeStampString); + } catch (NumberFormatException nfe) { + // Ignore we will just not restore the timestamp. + } + } + try { BrightnessConfiguration config = loadConfigurationFromXml(parser); - if (userSerial>= 0 && config != null) { + if (userSerial >= 0 && config != null) { mConfigurations.put(userSerial, config); + if (timeStamp != -1) { + mTimeStamps.put(userSerial, timeStamp); + } + if (packageName != null) { + mPackageNames.put(userSerial, packageName); + } } } catch (IllegalArgumentException iae) { Slog.e(TAG, "Failed to load brightness configuration!", iae); @@ -623,18 +660,24 @@ final class PersistentDataStore { private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); - final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + String description = null; + Pair<float[], float[]> curve = null; while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) { - Pair<float[], float[]> curve = loadCurveFromXml(parser, builder); - builder.setCurve(curve.first /*lux*/, curve.second /*nits*/); + description = parser.getAttributeValue(null, ATTR_DESCRIPTION); + curve = loadCurveFromXml(parser); } } + if (curve == null) { + return null; + } + final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( + curve.first, curve.second); + builder.setDescription(description); return builder.build(); } - private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser, - BrightnessConfiguration.Builder builder) + private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); List<Float> luxLevels = new ArrayList<>(); @@ -666,11 +709,19 @@ final class PersistentDataStore { public void saveToXml(XmlSerializer serializer) throws IOException { for (int i = 0; i < mConfigurations.size(); i++) { - final int userSerial= mConfigurations.keyAt(i); + final int userSerial = mConfigurations.keyAt(i); final BrightnessConfiguration config = mConfigurations.valueAt(i); serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION); serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial)); + String packageName = mPackageNames.get(userSerial); + if (packageName != null) { + serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); + } + long timestamp = mTimeStamps.get(userSerial, -1); + if (timestamp != -1) { + serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp)); + } saveConfigurationToXml(serializer, config); serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION); } @@ -679,6 +730,9 @@ final class PersistentDataStore { private static void saveConfigurationToXml(XmlSerializer serializer, BrightnessConfiguration config) throws IOException { serializer.startTag(null, TAG_BRIGHTNESS_CURVE); + if (config.getDescription() != null) { + serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription()); + } final Pair<float[], float[]> curve = config.getCurve(); for (int i = 0; i < curve.first.length; i++) { serializer.startTag(null, TAG_BRIGHTNESS_POINT); @@ -691,8 +745,16 @@ final class PersistentDataStore { public void dump(final PrintWriter pw, final String prefix) { for (int i = 0; i < mConfigurations.size(); i++) { - final int userSerial= mConfigurations.keyAt(i); + final int userSerial = mConfigurations.keyAt(i); + long time = mTimeStamps.get(userSerial, -1); + String packageName = mPackageNames.get(userSerial); pw.println(prefix + "User " + userSerial + ":"); + if (time != -1) { + pw.println(prefix + " set at: " + TimeUtils.formatForLogging(time)); + } + if (packageName != null) { + pw.println(prefix + " set by: " + packageName); + } pw.println(prefix + " " + mConfigurations.valueAt(i)); } } diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 97a6e850654b..db8dedbf12cc 100644..100755 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -228,12 +228,20 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { if (cmd.getOpcode() == Constants.MESSAGE_SET_OSD_NAME) { handleSetOsdName(cmd); return true; + } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) && + ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_OSD_NAME)) { + handleSetOsdName(cmd); + return true; } return false; case STATE_WAITING_FOR_VENDOR_ID: if (cmd.getOpcode() == Constants.MESSAGE_DEVICE_VENDOR_ID) { handleVendorId(cmd); return true; + } else if ((cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) && + ((cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID)) { + handleVendorId(cmd); + return true; } return false; case STATE_WAITING_FOR_DEVICE_POLLING: @@ -281,7 +289,11 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { String displayName = null; try { - displayName = new String(cmd.getParams(), "US-ASCII"); + if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) { + displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress); + } else { + displayName = new String(cmd.getParams(), "US-ASCII"); + } } catch (UnsupportedEncodingException e) { Slog.w(TAG, "Failed to decode display name: " + cmd.toString()); // If failed to get display name, use the default name of device. @@ -302,9 +314,12 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { return; } - byte[] params = cmd.getParams(); - int vendorId = HdmiUtils.threeBytesToInt(params); - current.mVendorId = vendorId; + if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) { + byte[] params = cmd.getParams(); + int vendorId = HdmiUtils.threeBytesToInt(params); + current.mVendorId = vendorId; + } + increaseProcessedDeviceCount(); checkAndProceedStage(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 81bccdc7dfaa..1e09383db56d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -698,10 +698,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { protected boolean handleReportAudioStatus(HdmiCecMessage message) { assertRunOnServiceThread(); - byte params[] = message.getParams(); - int mute = params[0] & 0x80; - int volume = params[0] & 0x7F; - setAudioStatus(mute == 0x80, volume); + boolean mute = HdmiUtils.isAudioStatusMute(message); + int volume = HdmiUtils.getAudioStatusVolume(message); + setAudioStatus(mute, volume); return true; } @@ -1004,6 +1003,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } void setAudioStatus(boolean mute, int volume) { + if (!isSystemAudioActivated()) { + return; + } synchronized (mLock) { mSystemAudioMute = mute; mSystemAudioVolume = volume; @@ -1019,6 +1021,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void changeVolume(int curVolume, int delta, int maxVolume) { assertRunOnServiceThread(); + if (getAvrDeviceInfo() == null) { + // On initialization process, getAvrDeviceInfo() may return null and cause exception + return; + } if (delta == 0 || !isSystemAudioActivated()) { return; } @@ -1048,6 +1054,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void changeMute(boolean mute) { assertRunOnServiceThread(); + if (getAvrDeviceInfo() == null) { + // On initialization process, getAvrDeviceInfo() may return null and cause exception + return; + } HdmiLogger.debug("[A]:Change mute:%b", mute); synchronized (mLock) { if (mSystemAudioMute == mute) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 807b1b19f870..3d079ccb0cad 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -989,8 +989,12 @@ public final class HdmiControlService extends SystemService { } // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing // volume change notification back to hdmi control service. - audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, - AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME); + int flag = AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME; + if (0 <= volume && volume <= 100) { + Slog.i(TAG, "volume: " + volume); + flag |= AudioManager.FLAG_SHOW_UI; + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, flag); + } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 8b1641187ac8..4ac3bba73e25 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -152,6 +152,32 @@ final class HdmiUtils { } /** + * Parse the <Report Audio Status> message and check if it is mute + * + * @param cmd the CEC message to parse + * @return true if the given parameter has [MUTE] + */ + static boolean isAudioStatusMute(HdmiCecMessage cmd) { + byte params[] = cmd.getParams(); + return (params[0] & 0x80) == 0x80; + } + + /** + * Parse the <Report Audio Status> message and extract the volume + * + * @param cmd the CEC message to parse + * @return device's volume. Constants.UNKNOWN_VOLUME in case it is out of range + */ + static int getAudioStatusVolume(HdmiCecMessage cmd) { + byte params[] = cmd.getParams(); + int volume = params[0] & 0x7F; + if (volume < 0x00 || 0x64 < volume) { + volume = Constants.UNKNOWN_VOLUME; + } + return volume; + } + + /** * Convert integer array to list of {@link Integer}. * * <p>The result is immutable. diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java index cab8439b6f92..d41a36ca031f 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java @@ -92,8 +92,8 @@ final class SystemAudioStatusAction extends HdmiCecFeatureAction { private void handleReportAudioStatus(HdmiCecMessage cmd) { byte[] params = cmd.getParams(); - boolean mute = (params[0] & 0x80) == 0x80; - int volume = params[0] & 0x7F; + boolean mute = HdmiUtils.isAudioStatusMute(cmd); + int volume = HdmiUtils.getAudioStatusVolume(cmd); tv().setAudioStatus(mute, volume); if (!(tv().isSystemAudioActivated() ^ mute)) { diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java index cd38b1fb2ac6..0011387f1c28 100644 --- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java +++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java @@ -139,8 +139,8 @@ final class VolumeControlAction extends HdmiCecFeatureAction { private boolean handleReportAudioStatus(HdmiCecMessage cmd) { byte params[] = cmd.getParams(); - boolean mute = (params[0] & 0x80) == 0x80; - int volume = params[0] & 0x7F; + boolean mute = HdmiUtils.isAudioStatusMute(cmd); + int volume = HdmiUtils.getAudioStatusVolume(cmd); mLastAvrVolume = volume; mLastAvrMute = mute; if (shouldUpdateAudioVolume(mute)) { diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java index 5f95f1a82fa8..1d053a500f92 100644 --- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -17,14 +17,11 @@ package com.android.server.job.controllers; import android.content.Context; -import android.os.IDeviceIdleController; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import com.android.internal.util.ArrayUtils; import com.android.server.ForceAppStandbyTracker; import com.android.server.ForceAppStandbyTracker.Listener; import com.android.server.job.JobSchedulerService; @@ -44,7 +41,6 @@ public final class BackgroundJobsController extends StateController { private static volatile BackgroundJobsController sController; private final JobSchedulerService mJobSchedulerService; - private final IDeviceIdleController mDeviceIdleController; private final ForceAppStandbyTracker mForceAppStandbyTracker; @@ -62,8 +58,6 @@ public final class BackgroundJobsController extends StateController { private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) { super(service, context, lock); mJobSchedulerService = service; - mDeviceIdleController = IDeviceIdleController.Stub.asInterface( - ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context); @@ -235,17 +229,23 @@ public final class BackgroundJobsController extends StateController { private final Listener mForceAppStandbyListener = new Listener() { @Override public void updateAllJobs() { - updateAllJobRestrictionsLocked(); + synchronized (mLock) { + updateAllJobRestrictionsLocked(); + } } @Override public void updateJobsForUid(int uid) { - updateJobRestrictionsForUidLocked(uid); + synchronized (mLock) { + updateJobRestrictionsForUidLocked(uid); + } } @Override public void updateJobsForUidPackage(int uid, String packageName) { - updateJobRestrictionsForUidLocked(uid); + synchronized (mLock) { + updateJobRestrictionsForUidLocked(uid); + } } }; } diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index 03fd7b3fffd9..373d87d971b8 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -16,6 +16,10 @@ package com.android.server.job.controllers; +import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; + import android.app.job.JobInfo; import android.content.Context; import android.net.ConnectivityManager; @@ -35,6 +39,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobServiceContext; import com.android.server.job.StateChangedListener; @@ -62,15 +67,15 @@ public final class ConnectivityController extends StateController implements private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>(); /** Singleton. */ - private static ConnectivityController mSingleton; + private static ConnectivityController sSingleton; private static Object sCreationLock = new Object(); public static ConnectivityController get(JobSchedulerService jms) { synchronized (sCreationLock) { - if (mSingleton == null) { - mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock()); + if (sSingleton == null) { + sSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock()); } - return mSingleton; + return sSingleton; } } @@ -105,37 +110,29 @@ public final class ConnectivityController extends StateController implements } /** - * Test to see if running the given job on the given network is sane. + * Test to see if running the given job on the given network is insane. * <p> * For example, if a job is trying to send 10MB over a 128Kbps EDGE * connection, it would take 10.4 minutes, and has no chance of succeeding * before the job times out, so we'd be insane to try running it. */ - private boolean isSane(JobStatus jobStatus, NetworkCapabilities capabilities) { + @SuppressWarnings("unused") + private static boolean isInsane(JobStatus jobStatus, Network network, + NetworkCapabilities capabilities) { final long estimatedBytes = jobStatus.getEstimatedNetworkBytes(); if (estimatedBytes == JobInfo.NETWORK_BYTES_UNKNOWN) { // We don't know how large the job is; cross our fingers! - return true; - } - if (capabilities == null) { - // We don't know what the network is like; cross our fingers! - return true; + return false; } // We don't ask developers to differentiate between upstream/downstream // in their size estimates, so test against the slowest link direction. - final long downstream = capabilities.getLinkDownstreamBandwidthKbps(); - final long upstream = capabilities.getLinkUpstreamBandwidthKbps(); - final long slowest; - if (downstream > 0 && upstream > 0) { - slowest = Math.min(downstream, upstream); - } else if (downstream > 0) { - slowest = downstream; - } else if (upstream > 0) { - slowest = upstream; - } else { + final long slowest = NetworkCapabilities.minBandwidth( + capabilities.getLinkDownstreamBandwidthKbps(), + capabilities.getLinkUpstreamBandwidthKbps()); + if (slowest == LINK_BANDWIDTH_UNSPECIFIED) { // We don't know what the network is like; cross our fingers! - return true; + return false; } final long estimatedMillis = ((estimatedBytes * DateUtils.SECOND_IN_MILLIS) @@ -144,28 +141,87 @@ public final class ConnectivityController extends StateController implements // If we'd never finish before the timeout, we'd be insane! Slog.w(TAG, "Estimated " + estimatedBytes + " bytes over " + slowest + " kbps network would take " + estimatedMillis + "ms; that's insane!"); + return true; + } else { return false; + } + } + + @SuppressWarnings("unused") + private static boolean isCongestionDelayed(JobStatus jobStatus, Network network, + NetworkCapabilities capabilities) { + // If network is congested, and job is less than 50% through the + // developer-requested window, then we're okay delaying the job. + if (!capabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)) { + return jobStatus.getFractionRunTime() < 0.5; } else { - return true; + return false; } } + @SuppressWarnings("unused") + private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, + NetworkCapabilities capabilities) { + return jobStatus.getJob().getRequiredNetwork().networkCapabilities + .satisfiedByNetworkCapabilities(capabilities); + } + + @SuppressWarnings("unused") + private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, + NetworkCapabilities capabilities) { + // Only consider doing this for prefetching jobs + if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) { + return false; + } + + // See if we match after relaxing any unmetered request + final NetworkCapabilities relaxed = new NetworkCapabilities( + jobStatus.getJob().getRequiredNetwork().networkCapabilities) + .removeCapability(NET_CAPABILITY_NOT_METERED); + if (relaxed.satisfiedByNetworkCapabilities(capabilities)) { + // TODO: treat this as "maybe" response; need to check quotas + return jobStatus.getFractionRunTime() > 0.5; + } else { + return false; + } + } + + @VisibleForTesting + static boolean isSatisfied(JobStatus jobStatus, Network network, + NetworkCapabilities capabilities) { + // Zeroth, we gotta have a network to think about being satisfied + if (network == null || capabilities == null) return false; + + // First, are we insane? + if (isInsane(jobStatus, network, capabilities)) return false; + + // Second, is the network congested? + if (isCongestionDelayed(jobStatus, network, capabilities)) return false; + + // Third, is the network a strict match? + if (isStrictSatisfied(jobStatus, network, capabilities)) return true; + + // Third, is the network a relaxed match? + if (isRelaxedSatisfied(jobStatus, network, capabilities)) return true; + + return false; + } + private boolean updateConstraintsSatisfied(JobStatus jobStatus) { // TODO: consider matching against non-active networks final int jobUid = jobStatus.getSourceUid(); final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked); final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked); final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); final boolean connected = (info != null) && info.isConnected(); - final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities - .satisfiedByNetworkCapabilities(capabilities); - final boolean sane = isSane(jobStatus, capabilities); + final boolean satisfied = isSatisfied(jobStatus, network, capabilities); final boolean changed = jobStatus - .setConnectivityConstraintSatisfied(connected && satisfied && sane); + .setConnectivityConstraintSatisfied(connected && satisfied); // Pass along the evaluated network for job to use; prevents race // conditions as default routes change over time, and opens the door to @@ -181,8 +237,7 @@ public final class ConnectivityController extends StateController implements if (DEBUG) { Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged") + " for " + jobStatus + ": connected=" + connected - + " satisfied=" + satisfied - + " sane=" + sane); + + " satisfied=" + satisfied); } return changed; } @@ -244,7 +299,7 @@ public final class ConnectivityController extends StateController implements } }; - private final INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() { + private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() { @Override public void onUidRulesChanged(int uid, int uidRules) { if (DEBUG) { @@ -254,11 +309,6 @@ public final class ConnectivityController extends StateController implements } @Override - public void onMeteredIfacesChanged(String[] meteredIfaces) { - // We track this via our NetworkCallback - } - - @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { if (DEBUG) { Slog.v(TAG, "Background restriction change to " + restrictBackground); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 8f68713945ca..0f5cb0a38c83 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -24,6 +24,7 @@ import android.app.job.JobInfo; import android.app.job.JobWorkItem; import android.content.ClipData; import android.content.ComponentName; +import android.content.pm.PackageManagerInternal; import android.net.Network; import android.net.Uri; import android.os.RemoteException; @@ -96,6 +97,7 @@ public final class JobStatus { final JobInfo job; /** Uid of the package requesting this job. */ final int callingUid; + final int targetSdkVersion; final String batteryName; final String sourcePackageName; @@ -243,12 +245,13 @@ public final class JobStatus { return callingUid; } - private JobStatus(JobInfo job, int callingUid, String sourcePackageName, + private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName, int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime) { this.job = job; this.callingUid = callingUid; + this.targetSdkVersion = targetSdkVersion; this.standbyBucket = standbyBucket; this.baseHeartbeat = heartbeat; @@ -307,7 +310,7 @@ public final class JobStatus { /** Copy constructor: used specifically when cloning JobStatus objects for persistence, * so we preserve RTC window bounds if the source object has them. */ public JobStatus(JobStatus jobStatus) { - this(jobStatus.getJob(), jobStatus.getUid(), + this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.targetSdkVersion, jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(), jobStatus.getSourceTag(), jobStatus.getNumFailures(), @@ -334,7 +337,7 @@ public final class JobStatus { long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, Pair<Long, Long> persistedExecutionTimesUTC) { - this(job, callingUid, sourcePkgName, sourceUserId, + this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId, standbyBucket, baseHeartbeat, sourceTag, 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, @@ -357,7 +360,7 @@ public final class JobStatus { long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt, long lastSuccessfulRunTime, long lastFailedRunTime) { - this(rescheduling.job, rescheduling.getUid(), + this(rescheduling.job, rescheduling.getUid(), resolveTargetSdkVersion(rescheduling.job), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getStandbyBucket(), newBaseHeartbeat, rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis, @@ -392,7 +395,7 @@ public final class JobStatus { sourceUserId, elapsedNow); JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); long currentHeartbeat = js != null ? js.currentHeartbeat() : 0; - return new JobStatus(job, callingUid, sourcePkg, sourceUserId, + return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId, standbyBucket, currentHeartbeat, tag, 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */); @@ -539,6 +542,10 @@ public final class JobStatus { return job.getId(); } + public int getTargetSdkVersion() { + return targetSdkVersion; + } + public void printUniqueId(PrintWriter pw) { UserHandle.formatUid(pw, callingUid); pw.print("/"); @@ -713,6 +720,37 @@ public final class JobStatus { return latestRunTimeElapsedMillis; } + /** + * Return the fractional position of "now" within the "run time" window of + * this job. + * <p> + * For example, if the earliest run time was 10 minutes ago, and the latest + * run time is 30 minutes from now, this would return 0.25. + * <p> + * If the job has no window defined, returns 1. When only an earliest or + * latest time is defined, it's treated as an infinitely small window at + * that time. + */ + public float getFractionRunTime() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + if (earliestRunTimeElapsedMillis == 0 && latestRunTimeElapsedMillis == Long.MAX_VALUE) { + return 1; + } else if (earliestRunTimeElapsedMillis == 0) { + return now >= latestRunTimeElapsedMillis ? 1 : 0; + } else if (latestRunTimeElapsedMillis == Long.MAX_VALUE) { + return now >= earliestRunTimeElapsedMillis ? 1 : 0; + } else { + if (now <= earliestRunTimeElapsedMillis) { + return 0; + } else if (now >= latestRunTimeElapsedMillis) { + return 1; + } else { + return (float) (now - earliestRunTimeElapsedMillis) + / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis); + } + } + } + public Pair<Long, Long> getPersistedUtcTimes() { return mPersistedUtcTimes; } @@ -1091,6 +1129,11 @@ public final class JobStatus { } } + private static int resolveTargetSdkVersion(JobInfo job) { + return LocalServices.getService(PackageManagerInternal.class) + .getPackageTargetSdkVersion(job.getService().getPackageName()); + } + // Dumpsys infrastructure public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) { pw.print(prefix); UserHandle.formatUid(pw, callingUid); diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java index 60b5b1f06c42..a61842b6d409 100644 --- a/services/core/java/com/android/server/location/ContextHubClientManager.java +++ b/services/core/java/com/android/server/location/ContextHubClientManager.java @@ -113,9 +113,7 @@ import java.util.function.Consumer; NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message); if (DEBUG_LOG_ENABLED) { - String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast"; - Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x" - + Long.toHexString(clientMessage.getNanoAppId())); + Log.v(TAG, "Received " + clientMessage); } if (clientMessage.isBroadcastMessage()) { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index e158819cd89f..48d275cc483f 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -827,7 +827,7 @@ public class GnssLocationProvider implements LocationProviderInterface { return isEnabled(); } }; - mGnssMetrics = new GnssMetrics(); + mGnssMetrics = new GnssMetrics(mBatteryStats); } /** @@ -2628,6 +2628,10 @@ public class GnssLocationProvider implements LocationProviderInterface { s.append(" mStarted=").append(mStarted).append('\n'); s.append(" mFixInterval=").append(mFixInterval).append('\n'); s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n'); + s.append(" mGnssMeasurementsProvider.isRegistered()=") + .append(mGnssMeasurementsProvider.isRegistered()).append('\n'); + s.append(" mGnssNavigationMessageProvider.isRegistered()=") + .append(mGnssNavigationMessageProvider.isRegistered()).append('\n'); s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n'); s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)); s.append(" ( "); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 58a95162ab82..fcdb9d1a87ca 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -46,7 +46,8 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>(); - private boolean mIsRegistered; // must access only on handler thread + private volatile boolean mIsRegistered; // must access only on handler thread, or read-only + private boolean mHasIsSupported; private boolean mIsSupported; @@ -58,6 +59,11 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { mTag = name; } + // read-only access for a dump() thread assured via volatile + public boolean isRegistered() { + return mIsRegistered; + } + public boolean addListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); IBinder binder = listener.asBinder(); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 879c0242d936..07ea51be4ba0 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -77,7 +77,7 @@ import android.security.KeyStore; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; -import android.security.keystore.KeychainProtectionParameter; +import android.security.keystore.KeychainProtectionParams; import android.security.keystore.UserNotAuthenticatedException; import android.security.keystore.WrappedApplicationKey; import android.security.keystore.KeychainSnapshot; @@ -1997,7 +1997,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public void setRecoverySecretTypes(@NonNull @KeychainProtectionParameter.UserSecretType + public void setRecoverySecretTypes(@NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes) throws RemoteException { mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes); } @@ -2014,7 +2014,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public void recoverySecretAvailable(@NonNull KeychainProtectionParameter recoverySecret) + public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret) throws RemoteException { mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret); } @@ -2022,12 +2022,16 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public byte[] startRecoverySession(@NonNull String sessionId, @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, - @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParameter> secrets) + @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParams> secrets) throws RemoteException { return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey, vaultParams, vaultChallenge, secrets); } + public void closeSession(@NonNull String sessionId) throws RemoteException { + mRecoverableKeyStoreManager.closeSession(sessionId); + } + @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index 38745f6cfd28..724073a87a70 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -16,12 +16,12 @@ package com.android.server.locksettings.recoverablekeystore; -import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN; +import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN; import android.annotation.Nullable; import android.content.Context; import android.security.keystore.KeyDerivationParams; -import android.security.keystore.KeychainProtectionParameter; +import android.security.keystore.KeychainProtectionParams; import android.security.keystore.KeychainSnapshot; import android.security.keystore.WrappedApplicationKey; import android.util.Log; @@ -174,8 +174,8 @@ public class KeySyncTask implements Runnable { return; } - byte[] deviceId = mRecoverableKeyStoreDb.getServerParams(mUserId, recoveryAgentUid); - if (deviceId == null) { + byte[] vaultHandle = mRecoverableKeyStoreDb.getServerParams(mUserId, recoveryAgentUid); + if (vaultHandle == null) { Log.w(TAG, "No device ID set for user " + mUserId); return; } @@ -228,11 +228,15 @@ public class KeySyncTask implements Runnable { counterId = generateAndStoreCounterId(recoveryAgentUid); } } + + // TODO: make sure the same counter id is used during recovery and remove temporary fix. + counterId = 1L; + byte[] vaultParams = KeySyncUtils.packVaultParams( publicKey, counterId, - deviceId, - TRUSTED_HARDWARE_MAX_ATTEMPTS); + TRUSTED_HARDWARE_MAX_ATTEMPTS, + vaultHandle); byte[] encryptedRecoveryKey; try { @@ -250,12 +254,12 @@ public class KeySyncTask implements Runnable { } // TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later // TODO: use Builder. - KeychainProtectionParameter metadata = new KeychainProtectionParameter( + KeychainProtectionParams metadata = new KeychainProtectionParams( /*userSecretType=*/ TYPE_LOCKSCREEN, /*lockScreenUiFormat=*/ getUiFormat(mCredentialType, mCredential), /*keyDerivationParams=*/ KeyDerivationParams.createSha256Params(salt), /*secret=*/ new byte[0]); - ArrayList<KeychainProtectionParameter> metadataList = new ArrayList<>(); + ArrayList<KeychainProtectionParams> metadataList = new ArrayList<>(); metadataList.add(metadata); int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid); @@ -263,12 +267,16 @@ public class KeySyncTask implements Runnable { // If application keys are not updated, snapshot will not be created on next unlock. mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false); - // TODO: use Builder. - mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot( - snapshotVersion, - /*recoveryMetadata=*/ metadataList, - /*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys), - /*encryptedRecoveryKeyblob=*/ encryptedRecoveryKey)); + mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot.Builder() + .setSnapshotVersion(snapshotVersion) + .setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS) + .setCounterId(counterId) + .setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey)) + .setServerParams(vaultHandle) + .setKeychainProtectionParams(metadataList) + .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) + .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey) + .build()); mSnapshotListenersStorage.recoverySnapshotAvailable(recoveryAgentUid); } @@ -307,7 +315,7 @@ public class KeySyncTask implements Runnable { */ private boolean shoudCreateSnapshot(int recoveryAgentUid) { int[] types = mRecoverableKeyStoreDb.getRecoverySecretTypes(mUserId, recoveryAgentUid); - if (!ArrayUtils.contains(types, KeychainProtectionParameter.TYPE_LOCKSCREEN)) { + if (!ArrayUtils.contains(types, KeychainProtectionParams.TYPE_LOCKSCREEN)) { // Only lockscreen type is supported. // We will need to pass extra argument to KeySyncTask to support custom pass phrase. return false; @@ -330,14 +338,14 @@ public class KeySyncTask implements Runnable { * @return The format - either pattern, pin, or password. */ @VisibleForTesting - @KeychainProtectionParameter.LockScreenUiFormat static int getUiFormat( + @KeychainProtectionParams.LockScreenUiFormat static int getUiFormat( int credentialType, String credential) { if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { - return KeychainProtectionParameter.TYPE_PATTERN; + return KeychainProtectionParams.TYPE_PATTERN; } else if (isPin(credential)) { - return KeychainProtectionParameter.TYPE_PIN; + return KeychainProtectionParams.TYPE_PIN; } else { - return KeychainProtectionParameter.TYPE_PASSWORD; + return KeychainProtectionParams.TYPE_PASSWORD; } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java index b4bef170e2bc..89e2debec9d2 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java @@ -61,7 +61,8 @@ public class KeySyncUtils { private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8); private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; - private static final int VAULT_PARAMS_LENGTH_BYTES = 85; + private static final int VAULT_PARAMS_LENGTH_BYTES = 94; + private static final int VAULT_HANDLE_LENGTH_BYTES = 17; /** * Encrypts the recovery key using both the lock screen hash and the remote storage's public @@ -287,18 +288,19 @@ public class KeySyncUtils { * * @param thmPublicKey Public key of the trusted hardware module. * @param counterId ID referring to the specific counter in the hardware module. - * @param deviceId ID of the device. * @param maxAttempts Maximum allowed guesses before trusted hardware wipes key. + * @param vaultHandle Handle of the Vault. * @return The binary vault params, ready for sync. */ public static byte[] packVaultParams( - PublicKey thmPublicKey, long counterId, byte[] deviceId, int maxAttempts) { + PublicKey thmPublicKey, long counterId, int maxAttempts, byte[] vaultHandle) { + // TODO: Check if vaultHandle has exactly the length of VAULT_HANDLE_LENGTH_BYTES somewhere return ByteBuffer.allocate(VAULT_PARAMS_LENGTH_BYTES) .order(ByteOrder.LITTLE_ENDIAN) .put(SecureBox.encodePublicKey(thmPublicKey)) .putLong(counterId) - .putLong(0L) // TODO: replace with device Id. .putInt(maxAttempts) + .put(vaultHandle) .array(); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index f14af4b52a2f..76508d5817e2 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -16,13 +16,12 @@ package com.android.server.locksettings.recoverablekeystore; -import static android.security.keystore.RecoveryManagerException.ERROR_BAD_X509_CERTIFICATE; -import static android.security.keystore.RecoveryManagerException.ERROR_DATABASE_ERROR; -import static android.security.keystore.RecoveryManagerException.ERROR_DECRYPTION_FAILED; -import static android.security.keystore.RecoveryManagerException.ERROR_INSECURE_USER; -import static android.security.keystore.RecoveryManagerException.ERROR_KEYSTORE_INTERNAL_ERROR; -import static android.security.keystore.RecoveryManagerException.ERROR_UNEXPECTED_MISSING_ALGORITHM; -import static android.security.keystore.RecoveryManagerException.ERROR_NO_SNAPSHOT_PENDING; +import static android.security.keystore.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT; +import static android.security.keystore.RecoveryController.ERROR_DECRYPTION_FAILED; +import static android.security.keystore.RecoveryController.ERROR_INSECURE_USER; +import static android.security.keystore.RecoveryController.ERROR_NO_SNAPSHOT_PENDING; +import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR; +import static android.security.keystore.RecoveryController.ERROR_SESSION_EXPIRED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,10 +33,10 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; -import android.security.keystore.KeychainProtectionParameter; +import android.security.keystore.KeychainProtectionParams; import android.security.keystore.KeychainSnapshot; +import android.security.keystore.RecoveryController; import android.security.keystore.WrappedApplicationKey; -import android.security.keystore.RecoveryManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -64,7 +63,7 @@ import java.util.concurrent.Executors; import javax.crypto.AEADBadTagException; /** - * Class with {@link RecoveryManager} API implementation and internal methods to interact + * Class with {@link RecoveryController} API implementation and internal methods to interact * with {@code LockSettingsService}. * * @hide @@ -98,7 +97,7 @@ public class RecoverableKeyStoreManager { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); } catch (KeyStoreException e) { - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } mInstance = new RecoverableKeyStoreManager( @@ -134,7 +133,7 @@ public class RecoverableKeyStoreManager { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "AES keygen algorithm not available. AOSP must support this.", e); - throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } } @@ -153,10 +152,10 @@ public class RecoverableKeyStoreManager { publicKey = kf.generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e); - throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } catch (InvalidKeySpecException e) { throw new ServiceSpecificException( - ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 certificate."); + ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 certificate."); } long updatedRows = mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey); if (updatedRows > 0) { @@ -257,7 +256,7 @@ public class RecoverableKeyStoreManager { * @hide */ public void setRecoverySecretTypes( - @NonNull @KeychainProtectionParameter.UserSecretType int[] secretTypes) + @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes) throws RemoteException { checkRecoverKeyStorePermission(); int userId = UserHandle.getCallingUserId(); @@ -292,9 +291,9 @@ public class RecoverableKeyStoreManager { } public void recoverySecretAvailable( - @NonNull KeychainProtectionParameter recoverySecret) throws RemoteException { + @NonNull KeychainProtectionParams recoverySecret) throws RemoteException { int uid = Binder.getCallingUid(); - if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParameter.TYPE_LOCKSCREEN) { + if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParams.TYPE_LOCKSCREEN) { throw new SecurityException( "Caller " + uid + " is not allowed to set lock screen secret"); } @@ -320,13 +319,14 @@ public class RecoverableKeyStoreManager { @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, - @NonNull List<KeychainProtectionParameter> secrets) + @NonNull List<KeychainProtectionParams> secrets) throws RemoteException { checkRecoverKeyStorePermission(); int uid = Binder.getCallingUid(); if (secrets.size() != 1) { - throw new UnsupportedOperationException("Only a single KeychainProtectionParameter is supported"); + throw new UnsupportedOperationException( + "Only a single KeychainProtectionParams is supported"); } PublicKey publicKey; @@ -336,13 +336,13 @@ public class RecoverableKeyStoreManager { // Should never happen throw new RuntimeException(e); } catch (InvalidKeySpecException e) { - throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 key"); + throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 key"); } // The raw public key bytes contained in vaultParams must match the ones given in // verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned // by the original recovery service. if (!publicKeysMatch(publicKey, vaultParams)) { - throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, + throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "The public keys given in verifierPublicKey and vaultParams do not match."); } @@ -362,9 +362,9 @@ public class RecoverableKeyStoreManager { keyClaimant); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e); - throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } catch (InvalidKeyException e) { - throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, e.getMessage()); + throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage()); } } @@ -390,7 +390,7 @@ public class RecoverableKeyStoreManager { int uid = Binder.getCallingUid(); RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId); if (sessionEntry == null) { - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, + throw new ServiceSpecificException(ERROR_SESSION_EXPIRED, String.format(Locale.US, "Application uid=%d does not have pending session '%s'", uid, sessionId)); } @@ -423,20 +423,25 @@ public class RecoverableKeyStoreManager { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException e) { - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } catch (InsecureUserException e) { throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); - } catch (KeyStoreException | InvalidKeyException e) { - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); - } catch (RecoverableKeyStorageException e) { - throw new ServiceSpecificException(ERROR_DATABASE_ERROR, e.getMessage()); + } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } } + /** + * Destroys the session with the given {@code sessionId}. + */ + public void closeSession(@NonNull String sessionId) throws RemoteException { + mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId); + } + public void removeKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); @@ -457,12 +462,12 @@ public class RecoverableKeyStoreManager { encryptedClaimResponse); return KeySyncUtils.decryptRecoveryKey(sessionEntry.getLskfHash(), locallyEncryptedKey); } catch (InvalidKeyException | AEADBadTagException e) { - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, + throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED, "Failed to decrypt recovery key " + e.getMessage()); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations - throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } } @@ -487,7 +492,7 @@ public class RecoverableKeyStoreManager { } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); throw new ServiceSpecificException( - ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); + ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } catch (InvalidKeyException | AEADBadTagException e) { throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED, "Failed to recover key with alias '" + alias + "': " + e.getMessage()); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java index 0042e101b4f1..c33c9de95182 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java @@ -16,8 +16,8 @@ package com.android.server.locksettings.recoverablekeystore; +import android.security.keystore.RecoveryController; import android.util.Log; -import android.security.keystore.RecoveryManager; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -97,7 +97,7 @@ public class WrappedKey { /*nonce=*/ cipher.getIV(), /*keyMaterial=*/ encryptedKeyMaterial, /*platformKeyGenerationId=*/ wrappingKey.getGenerationId(), - RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS); + RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); } /** @@ -107,14 +107,14 @@ public class WrappedKey { * @param keyMaterial The encrypted bytes of the key material. * @param platformKeyGenerationId The generation ID of the key used to wrap this key. * - * @see RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS + * @see RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS * @hide */ public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) { mNonce = nonce; mKeyMaterial = keyMaterial; mPlatformKeyGenerationId = platformKeyGenerationId; - mRecoveryStatus = RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS; + mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS; } /** diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java index 8bba2122787d..2b1416e06182 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java @@ -404,7 +404,7 @@ public class RecoverableKeyStoreDb { /** * Updates the list of user secret types used for end-to-end encryption. * If no secret types are set, recovery snapshot will not be created. - * See {@code KeychainProtectionParameter} + * See {@code KeychainProtectionParams} * * @param userId The userId of the profile the application is running under. * @param uid The uid of the application. diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java index f7633e4cee8d..0e66746f4160 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java @@ -73,6 +73,16 @@ public class RecoverySessionStorage implements Destroyable { } /** + * Deletes the session with {@code sessionId} created by app with {@code uid}. + */ + public void remove(int uid, String sessionId) { + if (mSessionsByUid.get(uid) == null) { + return; + } + mSessionsByUid.get(uid).removeIf(session -> session.mSessionId.equals(sessionId)); + } + + /** * Removes all sessions associated with the given recovery agent uid. * * @param uid The uid of the recovery agent whose sessions to remove. diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java index ee00fdc33367..68cd5e7aed78 100644 --- a/services/core/java/com/android/server/net/NetworkIdentitySet.java +++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java @@ -39,6 +39,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements private static final int VERSION_ADD_ROAMING = 2; private static final int VERSION_ADD_NETWORK_ID = 3; private static final int VERSION_ADD_METERED = 4; + private static final int VERSION_ADD_DEFAULT_NETWORK = 5; public NetworkIdentitySet() { } @@ -76,12 +77,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements metered = (type == TYPE_MOBILE); } - add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered)); + final boolean defaultNetwork; + if (version >= VERSION_ADD_DEFAULT_NETWORK) { + defaultNetwork = in.readBoolean(); + } else { + defaultNetwork = true; + } + + add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered, + defaultNetwork)); } } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION_ADD_METERED); + out.writeInt(VERSION_ADD_DEFAULT_NETWORK); out.writeInt(size()); for (NetworkIdentity ident : this) { out.writeInt(ident.getType()); @@ -90,6 +99,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements writeOptionalString(out, ident.getNetworkId()); out.writeBoolean(ident.getRoaming()); out.writeBoolean(ident.getMetered()); + out.writeBoolean(ident.getDefaultNetwork()); } } @@ -119,6 +129,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements return false; } + /** @return whether any {@link NetworkIdentity} in this set is considered on the default + network. */ + public boolean areAllMembersOnDefaultNetwork() { + if (isEmpty()) { + return true; + } + for (NetworkIdentity ident : this) { + if (!ident.getDefaultNetwork()) { + return false; + } + } + return true; + } + private static void writeOptionalString(DataOutputStream out, String value) throws IOException { if (value != null) { out.writeByte(1); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 7934a9682426..971ac8b922ef 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -16,6 +16,9 @@ package com.android.server.net; +import android.net.Network; +import android.telephony.SubscriptionPlan; + /** * Network Policy Manager local system service interface. * @@ -47,4 +50,25 @@ public abstract class NetworkPolicyManagerInternal { * @param added Denotes whether the {@param appId} has been added or removed from the whitelist. */ public abstract void onTempPowerSaveWhitelistChange(int appId, boolean added); + + /** + * Return the active {@link SubscriptionPlan} for the given network. + */ + public abstract SubscriptionPlan getSubscriptionPlan(Network network); + + public static final int QUOTA_TYPE_JOBS = 1; + public static final int QUOTA_TYPE_MULTIPATH = 2; + + /** + * Return the daily quota (in bytes) that can be opportunistically used on + * the given network to improve the end user experience. It's called + * "opportunistic" because it's traffic that would typically not use the + * given network. + */ + public abstract long getSubscriptionOpportunisticQuota(Network network, int quotaType); + + /** + * Informs that admin data is loaded and available. + */ + public abstract void onAdminDataAvailable(); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ff9b2fd4ae2d..e406d51ac823 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -34,6 +34,7 @@ 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.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -69,6 +70,7 @@ import static android.net.TrafficStats.MB_IN_BYTES; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.ArrayUtils.appendInt; @@ -97,6 +99,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -134,8 +137,10 @@ import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; +import android.net.NetworkSpecifier; import android.net.NetworkState; import android.net.NetworkTemplate; +import android.net.StringNetworkSpecifier; import android.net.TrafficStats; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -174,6 +179,7 @@ import android.text.format.Formatter; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.DataUnit; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Pair; @@ -182,6 +188,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import android.util.SparseLongArray; import android.util.TrustedTime; import android.util.Xml; @@ -192,6 +199,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; @@ -200,8 +208,10 @@ import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; +import com.android.server.SystemService; import libcore.io.IoUtils; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -219,7 +229,6 @@ import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Objects; @@ -278,6 +287,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public static final int TYPE_LIMIT = SystemMessage.NOTE_NET_LIMIT; @VisibleForTesting public static final int TYPE_LIMIT_SNOOZED = SystemMessage.NOTE_NET_LIMIT_SNOOZED; + @VisibleForTesting + public static final int TYPE_RAPID = SystemMessage.NOTE_NET_RAPID; private static final String TAG_POLICY_LIST = "policy-list"; private static final String TAG_NETWORK_POLICY = "network-policy"; @@ -323,6 +334,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; + /** + * Indicates the maximum wait time for admin data to be available; + */ + private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; + private static final int MSG_RULES_CHANGED = 1; private static final int MSG_METERED_IFACES_CHANGED = 2; private static final int MSG_LIMIT_REACHED = 5; @@ -332,6 +348,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; private static final int MSG_POLICIES_CHANGED = 13; private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15; + private static final int MSG_SUBSCRIPTION_OVERRIDE = 16; private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -373,6 +390,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final boolean mSuppressDefaultPolicy; + private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1); + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -384,6 +403,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mNetworkPoliciesSecondLock") final SparseArray<String> mSubscriptionPlansOwner = new SparseArray<>(); + /** Map from subId to daily opportunistic quota. */ + @GuardedBy("mNetworkPoliciesSecondLock") + final SparseLongArray mSubscriptionOpportunisticQuota = new SparseLongArray(); + /** Defined UID policies. */ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray(); /** Currently derived rules for each UID. */ @@ -453,6 +476,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); + /** Map from netId to subId as of last update */ + @GuardedBy("mNetworkPoliciesSecondLock") + private final SparseIntArray mNetIdToSubId = new SparseIntArray(); + private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<>(); @@ -653,6 +680,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mSystemReady = true; + waitForAdminData(); + // read policy from disk readPolicyAL(); @@ -984,6 +1013,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + @VisibleForTesting + public void updateNotifications() { + synchronized (mNetworkPoliciesSecondLock) { + updateNotificationsNL(); + } + } + /** * Check {@link NetworkPolicy} against current {@link INetworkStatsService} * to show visible notifications as needed. @@ -1028,6 +1064,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + // Alert the user about heavy recent data usage that might result in + // going over their carrier limit. + for (int i = 0; i < mNetIdToSubId.size(); i++) { + final int subId = mNetIdToSubId.valueAt(i); + final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); + if (plan == null) continue; + + final long limitBytes = plan.getDataLimitBytes(); + if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) { + // Ignore missing limits + } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { + // Unlimited data; no rapid usage alerting + } else { + // Warn if average usage over last 4 days is on track to blow + // pretty far past the plan limits. + final long recentDuration = TimeUnit.DAYS.toMillis(4); + final long end = RecurrenceRule.sClock.millis(); + final long start = end - recentDuration; + + final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll( + mContext.getSystemService(TelephonyManager.class).getSubscriberId(subId)); + final long recentBytes = getTotalBytes(template, start, end); + + final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); + final long cycleDuration = cycle.second.toInstant().toEpochMilli() + - cycle.first.toInstant().toEpochMilli(); + + final long projectedBytes = (recentBytes * cycleDuration) / recentDuration; + final long alertBytes = (limitBytes * 3) / 2; + if (projectedBytes > alertBytes) { + final NetworkPolicy policy = new NetworkPolicy(template, plan.getCycleRule(), + NetworkPolicy.WARNING_DISABLED, NetworkPolicy.LIMIT_DISABLED, + NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER, true, true); + enqueueNotification(policy, TYPE_RAPID, 0); + } + } + } + // cancel stale notifications that we didn't renew above for (int i = beforeNotifs.size()-1; i >= 0; i--) { final NotificationId notificationId = beforeNotifs.valueAt(i); @@ -1049,11 +1123,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SubscriptionManager sub = SubscriptionManager.from(mContext); // Mobile template is relevant when any active subscriber matches - final int[] subIds = sub.getActiveSubscriptionIdList(); + final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList()); for (int subId : subIds) { final String subscriberId = tele.getSubscriberId(subId); final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, + true); if (template.matches(probeIdent)) { return true; } @@ -1186,6 +1261,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); break; } + case TYPE_RAPID: { + final CharSequence title = res.getText(R.string.data_usage_rapid_title); + body = res.getText(R.string.data_usage_rapid_body); + + builder.setOngoing(true); + builder.setSmallIcon(R.drawable.stat_notify_error); + builder.setTicker(title); + builder.setContentTitle(title); + builder.setContentText(body); + + final Intent intent = buildViewDataUsageIntent(res, policy.template); + builder.setContentIntent(PendingIntent.getActivity( + mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } } // TODO: move to NotificationManager once we can mock it @@ -1239,6 +1329,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + @VisibleForTesting + public void updateNetworks() { + mConnReceiver.onReceive(null, null); + } + /** * Update mobile policies with data cycle information from {@link CarrierConfigManager} * if necessary. @@ -1254,7 +1349,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // find and update the mobile NetworkPolicy for this subscriber id final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -1457,11 +1552,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SubscriptionManager sm = SubscriptionManager.from(mContext); final TelephonyManager tm = TelephonyManager.from(mContext); - final int[] subIds = sm.getActiveSubscriptionIdList(); + final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList()); for (int subId : subIds) { final String subscriberId = tm.getSubscriberId(subId); final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, + true); // Template is matched when subscriber id matches. if (template.matches(probeIdent)) { tm.setPolicyDataEnabled(enabled, subId); @@ -1496,7 +1592,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final NetworkState[] states; try { - states = mConnManager.getAllNetworkState(); + states = defeatNullable(mConnManager.getAllNetworkState()); } catch (RemoteException e) { // ignored; service lives in system_server return; @@ -1504,10 +1600,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // First, generate identities of all connected networks so we can // quickly compare them against all defined policies below. + mNetIdToSubId.clear(); final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>(); for (NetworkState state : states) { + if (state.network != null) { + mNetIdToSubId.put(state.network.netId, parseSubId(state)); + } if (state.networkInfo != null && state.networkInfo.isConnected()) { - final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state); + final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, + true); identified.put(state, ident); } } @@ -1607,6 +1708,42 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } mMeteredIfaces = newMeteredIfaces; + // Finally, calculate our opportunistic quotas + // TODO: add experiments support to disable or tweak ratios + mSubscriptionOpportunisticQuota.clear(); + for (NetworkState state : states) { + if (state.network == null) continue; + final int subId = getSubIdLocked(state.network); + final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId); + if (plan == null) continue; + + // By default assume we have no quota + long quotaBytes = 0; + + final long limitBytes = plan.getDataLimitBytes(); + if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) { + // Ignore missing limits + } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) { + // Unlimited data; let's use 20MiB/day (600MiB/month) + quotaBytes = DataUnit.MEBIBYTES.toBytes(20); + } else { + // Limited data; let's only use 10% of remaining budget + final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next(); + final long start = cycle.first.toInstant().toEpochMilli(); + final long end = cycle.second.toInstant().toEpochMilli(); + final long totalBytes = getTotalBytes( + NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end); + final long remainingBytes = limitBytes - totalBytes; + final long remainingDays = Math.min(1, (end - RecurrenceRule.sClock.millis()) + / TimeUnit.DAYS.toMillis(1)); + if (remainingBytes > 0) { + quotaBytes = (remainingBytes / remainingDays) / 10; + } + } + + mSubscriptionOpportunisticQuota.put(subId, quotaBytes); + } + final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); @@ -1624,7 +1761,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final TelephonyManager tele = TelephonyManager.from(mContext); final SubscriptionManager sub = SubscriptionManager.from(mContext); - final int[] subIds = sub.getActiveSubscriptionIdList(); + final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList()); for (int subId : subIds) { final String subscriberId = tele.getSubscriberId(subId); ensureActiveMobilePolicyAL(subId, subscriberId); @@ -1642,7 +1779,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) { // Poke around to see if we already have a policy final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -2815,6 +2952,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override + public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, + long timeoutMillis, String callingPackage) { + enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); + + // We can only override when carrier told us about plans + synchronized (mNetworkPoliciesSecondLock) { + if (ArrayUtils.isEmpty(mSubscriptionPlans.get(subId))) { + throw new IllegalStateException( + "Must provide SubscriptionPlan information before overriding"); + } + } + + mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, + overrideMask, overrideValue, subId)); + if (timeoutMillis > 0) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, + overrideMask, 0, subId), timeoutMillis); + } + } + + @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; @@ -3819,6 +3977,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId, + int overrideMask, int overrideValue) { + if (listener != null) { + try { + listener.onSubscriptionOverride(subId, overrideMask, overrideValue); + } catch (RemoteException ignored) { + } + } + } + private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { @@ -3922,6 +4090,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { resetUidFirewallRules(msg.arg1); return true; } + case MSG_SUBSCRIPTION_OVERRIDE: { + final int overrideMask = msg.arg1; + final int overrideValue = msg.arg2; + final int subId = (int) msg.obj; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue); + } + mListeners.finishBroadcast(); + return true; + } default: { return false; } @@ -4404,12 +4584,72 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForTempWhitelistChangeUL(appId); } } + + @Override + public SubscriptionPlan getSubscriptionPlan(Network network) { + synchronized (mNetworkPoliciesSecondLock) { + final int subId = getSubIdLocked(network); + return getPrimarySubscriptionPlanLocked(subId); + } + } + + @Override + public long getSubscriptionOpportunisticQuota(Network network, int quotaType) { + synchronized (mNetworkPoliciesSecondLock) { + // TODO: handle splitting quota between use-cases + return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network)); + } + } + + @Override + public void onAdminDataAvailable() { + mAdminDataAvailableLatch.countDown(); + } + } + + private int parseSubId(NetworkState state) { + // TODO: moved to using a legitimate NetworkSpecifier instead of string parsing + int subId = INVALID_SUBSCRIPTION_ID; + if (state != null && state.networkCapabilities != null + && state.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier(); + if (spec instanceof StringNetworkSpecifier) { + try { + subId = Integer.parseInt(((StringNetworkSpecifier) spec).specifier); + } catch (NumberFormatException e) { + } + } + } + return subId; + } + + private int getSubIdLocked(Network network) { + return mNetIdToSubId.get(network.netId, INVALID_SUBSCRIPTION_ID); + } + + private SubscriptionPlan getPrimarySubscriptionPlanLocked(int subId) { + final SubscriptionPlan[] plans = mSubscriptionPlans.get(subId); + return ArrayUtils.isEmpty(plans) ? null : plans[0]; + } + + /** + * This will only ever be called once - during device boot. + */ + private void waitForAdminData() { + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) { + ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch, + WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data"); + } } private static boolean hasRule(int uidRules, int rule) { return (uidRules & rule) != 0; } + private static @NonNull NetworkState[] defeatNullable(@Nullable NetworkState[] val) { + return (val != null) ? val : new NetworkState[0]; + } + private class NotificationId { private final String mTag; private final int mId; diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index 4ceb592af055..961a45177897 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -17,6 +17,8 @@ package com.android.server.net; import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkStats.ROAMING_NO; @@ -364,6 +366,8 @@ public class NetworkStatsCollection implements FileRotator.Reader { entry.uid = key.uid; entry.set = key.set; entry.tag = key.tag; + entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork() ? + DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO; entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO; entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO; entry.rxBytes = historyEntry.rxBytes; diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index db61ef5cd9b9..78fd4b49237a 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -25,6 +25,7 @@ import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.ROAMING_ALL; @@ -83,6 +84,7 @@ import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; import android.net.LinkProperties; +import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; import android.net.NetworkInfo; @@ -231,14 +233,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final Object mStatsLock = new Object(); /** Set of currently active ifaces. */ + @GuardedBy("mStatsLock") private final ArrayMap<String, NetworkIdentitySet> mActiveIfaces = new ArrayMap<>(); + /** Set of currently active ifaces for UID stats. */ + @GuardedBy("mStatsLock") private final ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces = new ArrayMap<>(); + /** Current default active iface. */ private String mActiveIface; + /** Set of any ifaces associated with mobile networks since boot. */ + @GuardedBy("mStatsLock") private String[] mMobileIfaces = new String[0]; + /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */ + @GuardedBy("mStatsLock") + private Network[] mDefaultNetworks = new Network[0]; + private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); @@ -666,9 +678,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); final NetworkStats stats = new NetworkStats(end - start, 1); - stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, - ROAMING_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets, - entry.operations)); + stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets, + entry.txBytes, entry.txPackets, entry.operations)); return stats; } @@ -779,13 +791,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public void forceUpdateIfaces() { + public void forceUpdateIfaces(Network[] defaultNetworks) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); assertBandwidthControlEnabled(); final long token = Binder.clearCallingIdentity(); try { - updateIfaces(); + updateIfaces(defaultNetworks); } finally { Binder.restoreCallingIdentity(token); } @@ -996,11 +1008,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; - private void updateIfaces() { + private void updateIfaces(Network[] defaultNetworks) { synchronized (mStatsLock) { mWakeLock.acquire(); try { - updateIfacesLocked(); + updateIfacesLocked(defaultNetworks); } finally { mWakeLock.release(); } @@ -1013,7 +1025,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * are active on a single {@code iface}, they are combined under a single * {@link NetworkIdentitySet}. */ - private void updateIfacesLocked() { + private void updateIfacesLocked(Network[] defaultNetworks) { if (!mSystemReady) return; if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); @@ -1040,12 +1052,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // Rebuild active interfaces based on connected networks mActiveIfaces.clear(); mActiveUidIfaces.clear(); + if (defaultNetworks != null) { + // Caller is ConnectivityService. Update the list of default networks. + mDefaultNetworks = defaultNetworks; + } final ArraySet<String> mobileIfaces = new ArraySet<>(); for (NetworkState state : states) { if (state.networkInfo.isConnected()) { final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType()); - final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state); + final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network); + final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, + isDefault); // Traffic occurring on the base interface is always counted for // both total usage and UID details. @@ -1065,7 +1083,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // Copy the identify from IMS one but mark it as metered. NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), - ident.getRoaming(), true); + ident.getRoaming(), true /* metered */, + true /* onDefaultNetwork */); findOrCreateNetworkIdentitySet(mActiveIfaces, VT_INTERFACE).add(vtIdent); findOrCreateNetworkIdentitySet(mActiveUidIfaces, VT_INTERFACE).add(vtIdent); } @@ -1511,7 +1530,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return true; } case MSG_UPDATE_IFACES: { - mService.updateIfaces(); + mService.updateIfaces(null); return true; } case MSG_REGISTER_GLOBAL_ALERT: { diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java index 3bcc36f0ba2c..239ddbeb5f86 100644 --- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java +++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java @@ -17,15 +17,11 @@ package com.android.server.net.watchlist; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.net.IIpConnectivityMetrics; import android.net.INetdEventCallback; -import android.net.NetworkWatchlistManager; import android.net.metrics.IpConnectivityLog; import android.os.Binder; import android.os.Process; -import android.os.SharedMemory; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -42,9 +38,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; -import java.util.List; /** * Implementation of network watchlist service. @@ -99,7 +93,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { private volatile boolean mIsLoggingEnabled = false; private final Object mLoggingSwitchLock = new Object(); - private final WatchlistSettings mSettings; + private final WatchlistConfig mConfig; private final Context mContext; // Separate thread to handle expensive watchlist logging work. @@ -112,7 +106,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { public NetworkWatchlistService(Context context) { mContext = context; - mSettings = WatchlistSettings.getInstance(); + mConfig = WatchlistConfig.getInstance(); mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, /* allowIo */ false); mHandlerThread.start(); @@ -126,7 +120,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { NetworkWatchlistService(Context context, ServiceThread handlerThread, WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) { mContext = context; - mSettings = WatchlistSettings.getInstance(); + mConfig = WatchlistConfig.getInstance(); mHandlerThread = handlerThread; mNetworkWatchlistHandler = handler; mIpConnectivityMetrics = ipConnectivityMetrics; @@ -228,7 +222,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { public void reloadWatchlist() throws RemoteException { enforceWatchlistLoggingPermission(); Slog.i(TAG, "Reloading watchlist"); - mSettings.reloadSettings(); + mConfig.reloadConfig(); } @Override @@ -240,7 +234,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - mSettings.dump(fd, pw, args); + mConfig.dump(fd, pw, args); } } diff --git a/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java new file mode 100644 index 000000000000..c1231fa342e7 --- /dev/null +++ b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright 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.net.watchlist; + +import android.privacy.DifferentialPrivacyEncoder; +import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig; +import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Helper class to apply differential privacy to watchlist reports. + */ +class PrivacyUtils { + + private static final String TAG = "PrivacyUtils"; + + /** + * Parameters used for encoding watchlist reports. + * These numbers are optimal parameters for protecting privacy with good utility. + * + * TODO: Add links to explain the math behind. + */ + private static final String ENCODER_ID_PREFIX = "watchlist_encoder:"; + private static final double PROB_F = 0.469; + private static final double PROB_P = 0.28; + private static final double PROB_Q = 1.0; + + private PrivacyUtils() { + } + + /** + * Get insecure DP encoder. + * Should not apply it directly on real data as seed is not randomized. + */ + @VisibleForTesting + static DifferentialPrivacyEncoder createInsecureDPEncoderForTest(String appDigest) { + final LongitudinalReportingConfig config = createLongitudinalReportingConfig(appDigest); + return LongitudinalReportingEncoder.createInsecureEncoderForTest(config); + } + + /** + * Get secure encoder to encode watchlist. + * + * Warning: If you use the same user secret and app digest, then you will get the same + * PRR result. + */ + @VisibleForTesting + static DifferentialPrivacyEncoder createSecureDPEncoder(byte[] userSecret, + String appDigest) { + final LongitudinalReportingConfig config = createLongitudinalReportingConfig(appDigest); + return LongitudinalReportingEncoder.createEncoder(config, userSecret); + } + + /** + * Get DP config for encoding watchlist reports. + */ + private static LongitudinalReportingConfig createLongitudinalReportingConfig(String appDigest) { + return new LongitudinalReportingConfig(ENCODER_ID_PREFIX + appDigest, PROB_F, PROB_P, + PROB_Q); + } + + /** + * Create a map that stores appDigest, encoded_visitedWatchlist pairs. + */ + @VisibleForTesting + static Map<String, Boolean> createDpEncodedReportMap(boolean isSecure, byte[] userSecret, + List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) { + final int appDigestListSize = appDigestList.size(); + final HashMap<String, Boolean> resultMap = new HashMap<>(appDigestListSize); + for (int i = 0; i < appDigestListSize; i++) { + final String appDigest = appDigestList.get(i); + // Each app needs to have different PRR result, hence we use appDigest as encoder Id. + final DifferentialPrivacyEncoder encoder = isSecure + ? createSecureDPEncoder(userSecret, appDigest) + : createInsecureDPEncoderForTest(appDigest); + final boolean visitedWatchlist = aggregatedResult.appDigestList.contains(appDigest); + // Get the least significant bit of first byte, and set result to True if it is 1 + boolean encodedVisitedWatchlist = ((int) encoder.encodeBoolean(visitedWatchlist)[0] + & 0x1) == 0x1; + resultMap.put(appDigest, encodedVisitedWatchlist); + } + return resultMap; + } +} diff --git a/services/core/java/com/android/server/net/watchlist/ReportEncoder.java b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java new file mode 100644 index 000000000000..5d7ff5a751aa --- /dev/null +++ b/services/core/java/com/android/server/net/watchlist/ReportEncoder.java @@ -0,0 +1,126 @@ +/* + * Copyright 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.net.watchlist; + +import android.annotation.Nullable; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.HexDump; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Helper class to encode and generate serialized DP encoded watchlist report. + * + * <p>Serialized report data structure: + * [4 bytes magic number][4_bytes_report_version_code][32_bytes_watchlist_hash] + * [app_1_digest_byte_array][app_1_encoded_visited_cnc_byte] + * [app_2_digest_byte_array][app_2_encoded_visited_cnc_byte] + * ... + * + * Total size: 4 + 4 + 32 + (32+1)*N, where N = number of digests + */ +class ReportEncoder { + + private static final String TAG = "ReportEncoder"; + + // Report header magic number + private static final byte[] MAGIC_NUMBER = {(byte) 0x8D, (byte) 0x37, (byte) 0x0A, (byte) 0xAC}; + // Report version number, as file format / parameters can be changed in later version, we need + // to have versioning on watchlist report format + private static final byte[] REPORT_VERSION = {(byte) 0x00, (byte) 0x01}; + + private static final int WATCHLIST_HASH_SIZE = 32; + private static final int APP_DIGEST_SIZE = 32; + + /** + * Apply DP on watchlist results, and generate a serialized watchlist report ready to store + * in DropBox. + */ + static byte[] encodeWatchlistReport(WatchlistConfig config, byte[] userSecret, + List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) { + Map<String, Boolean> resultMap = PrivacyUtils.createDpEncodedReportMap( + config.isConfigSecure(), userSecret, appDigestList, aggregatedResult); + return serializeReport(config, resultMap); + } + + /** + * Convert DP encoded watchlist report into byte[] format. + * TODO: Serialize it using protobuf + * + * @param encodedReportMap DP encoded watchlist report. + * @return Watchlist report in byte[] format, which will be shared in Dropbox. Null if + * watchlist report cannot be generated. + */ + @Nullable + @VisibleForTesting + static byte[] serializeReport(WatchlistConfig config, + Map<String, Boolean> encodedReportMap) { + // TODO: Handle watchlist config changed case + final byte[] watchlistHash = config.getWatchlistConfigHash(); + if (watchlistHash == null) { + Log.e(TAG, "No watchlist hash"); + return null; + } + if (watchlistHash.length != WATCHLIST_HASH_SIZE) { + Log.e(TAG, "Unexpected hash length"); + return null; + } + final int reportMapSize = encodedReportMap.size(); + final byte[] outputReport = + new byte[MAGIC_NUMBER.length + REPORT_VERSION.length + WATCHLIST_HASH_SIZE + + reportMapSize * (APP_DIGEST_SIZE + /* Result */ 1)]; + final List<String> sortedKeys = new ArrayList(encodedReportMap.keySet()); + Collections.sort(sortedKeys); + + int offset = 0; + + // Set magic number to report + System.arraycopy(MAGIC_NUMBER, 0, outputReport, offset, MAGIC_NUMBER.length); + offset += MAGIC_NUMBER.length; + + // Set report version to report + System.arraycopy(REPORT_VERSION, 0, outputReport, offset, REPORT_VERSION.length); + offset += REPORT_VERSION.length; + + // Set watchlist hash to report + System.arraycopy(watchlistHash, 0, outputReport, offset, watchlistHash.length); + offset += watchlistHash.length; + + // Set app digest, encoded_isPha pair to report + for (int i = 0; i < reportMapSize; i++) { + String key = sortedKeys.get(i); + byte[] digest = HexDump.hexStringToByteArray(key); + boolean isPha = encodedReportMap.get(key); + System.arraycopy(digest, 0, outputReport, offset, APP_DIGEST_SIZE); + offset += digest.length; + outputReport[offset] = (byte) (isPha ? 1 : 0); + offset += 1; + } + if (outputReport.length != offset) { + Log.e(TAG, "Watchlist report size does not match! Offset: " + offset + ", report size: " + + outputReport.length); + + } + return outputReport; + } +} diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java new file mode 100644 index 000000000000..7387ad4f90ff --- /dev/null +++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java @@ -0,0 +1,253 @@ +/* + * 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.net.watchlist; + +import android.util.AtomicFile; +import android.util.Log; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.HexDump; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.CRC32; + +/** + * Class for watchlist config operations, like setting watchlist, query if a domain + * exists in watchlist. + */ +class WatchlistConfig { + private static final String TAG = "WatchlistConfig"; + + // Watchlist config that pushed by ConfigUpdater. + private static final String NETWORK_WATCHLIST_DB_PATH = + "/data/misc/network_watchlist/network_watchlist.xml"; + + // Hash for null / unknown config, a 32 byte array filled with content 0x00 + private static final byte[] UNKNOWN_CONFIG_HASH = new byte[32]; + + private static class XmlTags { + private static final String WATCHLIST_CONFIG = "watchlist-config"; + private static final String SHA256_DOMAIN = "sha256-domain"; + private static final String CRC32_DOMAIN = "crc32-domain"; + private static final String SHA256_IP = "sha256-ip"; + private static final String CRC32_IP = "crc32-ip"; + private static final String HASH = "hash"; + } + + private static class CrcShaDigests { + final HarmfulDigests crc32Digests; + final HarmfulDigests sha256Digests; + + public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) { + this.crc32Digests = crc32Digests; + this.sha256Digests = sha256Digests; + } + } + + /* + * This is always true unless watchlist is being set by adb command, then it will be false + * until next reboot. + */ + private boolean mIsSecureConfig = true; + + private final static WatchlistConfig sInstance = new WatchlistConfig(); + private final File mXmlFile; + + private volatile CrcShaDigests mDomainDigests; + private volatile CrcShaDigests mIpDigests; + + public static WatchlistConfig getInstance() { + return sInstance; + } + + private WatchlistConfig() { + this(new File(NETWORK_WATCHLIST_DB_PATH)); + } + + @VisibleForTesting + protected WatchlistConfig(File xmlFile) { + mXmlFile = xmlFile; + reloadConfig(); + } + + /** + * Reload watchlist by reading config file. + */ + public void reloadConfig() { + try (FileInputStream stream = new FileInputStream(mXmlFile)){ + final List<byte[]> crc32DomainList = new ArrayList<>(); + final List<byte[]> sha256DomainList = new ArrayList<>(); + final List<byte[]> crc32IpList = new ArrayList<>(); + final List<byte[]> sha256IpList = new ArrayList<>(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + parser.nextTag(); + parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_CONFIG); + while (parser.nextTag() == XmlPullParser.START_TAG) { + String tagName = parser.getName(); + switch (tagName) { + case XmlTags.CRC32_DOMAIN: + parseHashes(parser, tagName, crc32DomainList); + break; + case XmlTags.CRC32_IP: + parseHashes(parser, tagName, crc32IpList); + break; + case XmlTags.SHA256_DOMAIN: + parseHashes(parser, tagName, sha256DomainList); + break; + case XmlTags.SHA256_IP: + parseHashes(parser, tagName, sha256IpList); + break; + default: + Log.w(TAG, "Unknown element: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG); + mDomainDigests = new CrcShaDigests(new HarmfulDigests(crc32DomainList), + new HarmfulDigests(sha256DomainList)); + mIpDigests = new CrcShaDigests(new HarmfulDigests(crc32IpList), + new HarmfulDigests(sha256IpList)); + Log.i(TAG, "Reload watchlist done"); + } catch (IllegalStateException | NullPointerException | NumberFormatException | + XmlPullParserException | IOException | IndexOutOfBoundsException e) { + Slog.e(TAG, "Failed parsing xml", e); + } + } + + private void parseHashes(XmlPullParser parser, String tagName, List<byte[]> hashList) + throws IOException, XmlPullParserException { + parser.require(XmlPullParser.START_TAG, null, tagName); + // Get all the hashes for this tag + while (parser.nextTag() == XmlPullParser.START_TAG) { + parser.require(XmlPullParser.START_TAG, null, XmlTags.HASH); + byte[] hash = HexDump.hexStringToByteArray(parser.nextText()); + parser.require(XmlPullParser.END_TAG, null, XmlTags.HASH); + hashList.add(hash); + } + parser.require(XmlPullParser.END_TAG, null, tagName); + } + + public boolean containsDomain(String domain) { + final CrcShaDigests domainDigests = mDomainDigests; + if (domainDigests == null) { + // mDomainDigests is not initialized + return false; + } + // First it does a quick CRC32 check. + final byte[] crc32 = getCrc32(domain); + if (!domainDigests.crc32Digests.contains(crc32)) { + return false; + } + // Now we do a slow SHA256 check. + final byte[] sha256 = getSha256(domain); + return domainDigests.sha256Digests.contains(sha256); + } + + public boolean containsIp(String ip) { + final CrcShaDigests ipDigests = mIpDigests; + if (ipDigests == null) { + // mIpDigests is not initialized + return false; + } + // First it does a quick CRC32 check. + final byte[] crc32 = getCrc32(ip); + if (!ipDigests.crc32Digests.contains(crc32)) { + return false; + } + // Now we do a slow SHA256 check. + final byte[] sha256 = getSha256(ip); + return ipDigests.sha256Digests.contains(sha256); + } + + + /** Get CRC32 of a string + * + * TODO: Review if we should use CRC32 or other algorithms + */ + private byte[] getCrc32(String str) { + final CRC32 crc = new CRC32(); + crc.update(str.getBytes()); + final long tmp = crc.getValue(); + return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255), + (byte) (tmp >> 8 & 255), (byte) (tmp & 255)}; + } + + /** Get SHA256 of a string */ + private byte[] getSha256(String str) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA256"); + } catch (NoSuchAlgorithmException e) { + /* can't happen */ + return null; + } + messageDigest.update(str.getBytes()); + return messageDigest.digest(); + } + + public boolean isConfigSecure() { + return mIsSecureConfig; + } + + public byte[] getWatchlistConfigHash() { + if (!mXmlFile.exists()) { + return UNKNOWN_CONFIG_HASH; + } + try { + return DigestUtils.getSha256Hash(mXmlFile); + } catch (IOException | NoSuchAlgorithmException e) { + Log.e(TAG, "Unable to get watchlist config hash", e); + } + return UNKNOWN_CONFIG_HASH; + } + + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Domain CRC32 digest list:"); + if (mDomainDigests != null) { + mDomainDigests.crc32Digests.dump(fd, pw, args); + } + pw.println("Domain SHA256 digest list:"); + if (mDomainDigests != null) { + mDomainDigests.sha256Digests.dump(fd, pw, args); + } + pw.println("Ip CRC32 digest list:"); + if (mIpDigests != null) { + mIpDigests.crc32Digests.dump(fd, pw, args); + } + pw.println("Ip SHA256 digest list:"); + if (mIpDigests != null) { + mIpDigests.sha256Digests.dump(fd, pw, args); + } + } +} diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java index 2247558476c4..ee0049be8584 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java @@ -16,8 +16,10 @@ package com.android.server.net.watchlist; +import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; @@ -30,14 +32,19 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.HexDump; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** @@ -57,14 +64,17 @@ class WatchlistLoggingHandler extends Handler { private static final String DROPBOX_TAG = "network_watchlist_report"; private final Context mContext; + private final @Nullable DropBoxManager mDropBoxManager; private final ContentResolver mResolver; private final PackageManager mPm; private final WatchlistReportDbHelper mDbHelper; + private final WatchlistConfig mConfig; private final WatchlistSettings mSettings; // A cache for uid and apk digest mapping. // As uid won't be reused until reboot, it's safe to assume uid is unique per signature and app. // TODO: Use more efficient data structure. - private final HashMap<Integer, byte[]> mCachedUidDigestMap = new HashMap<>(); + private final ConcurrentHashMap<Integer, byte[]> mCachedUidDigestMap = + new ConcurrentHashMap<>(); private interface WatchlistEventKeys { String HOST = "host"; @@ -79,7 +89,9 @@ class WatchlistLoggingHandler extends Handler { mPm = mContext.getPackageManager(); mResolver = mContext.getContentResolver(); mDbHelper = WatchlistReportDbHelper.getInstance(context); + mConfig = WatchlistConfig.getInstance(); mSettings = WatchlistSettings.getInstance(); + mDropBoxManager = mContext.getSystemService(DropBoxManager.class); } @Override @@ -162,69 +174,88 @@ class WatchlistLoggingHandler extends Handler { } private void tryAggregateRecords() { - if (shouldReportNetworkWatchlist()) { - Slog.i(TAG, "Start aggregating watchlist records."); - final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class); - if (dbox != null && !dbox.isTagEnabled(DROPBOX_TAG)) { - final WatchlistReportDbHelper.AggregatedResult aggregatedResult = - mDbHelper.getAggregatedRecords(); - final byte[] encodedResult = encodeAggregatedResult(aggregatedResult); - if (encodedResult != null) { - addEncodedReportToDropBox(encodedResult); - } - } - mDbHelper.cleanup(); - Settings.Global.putLong(mResolver, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, - System.currentTimeMillis()); - } else { + // Check if it's necessary to generate watchlist report now. + if (!shouldReportNetworkWatchlist()) { Slog.i(TAG, "No need to aggregate record yet."); + return; } + Slog.i(TAG, "Start aggregating watchlist records."); + if (mDropBoxManager != null && mDropBoxManager.isTagEnabled(DROPBOX_TAG)) { + Settings.Global.putLong(mResolver, + Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, + System.currentTimeMillis()); + final WatchlistReportDbHelper.AggregatedResult aggregatedResult = + mDbHelper.getAggregatedRecords(); + // Get all digests for watchlist report, it should include all installed + // application digests and previously recorded app digests. + final List<String> digestsForReport = getAllDigestsForReport(aggregatedResult); + final byte[] secretKey = mSettings.getPrivacySecretKey(); + final byte[] encodedResult = ReportEncoder.encodeWatchlistReport(mConfig, + secretKey, digestsForReport, aggregatedResult); + if (encodedResult != null) { + addEncodedReportToDropBox(encodedResult); + } + } + mDbHelper.cleanup(); } - private byte[] encodeAggregatedResult( - WatchlistReportDbHelper.AggregatedResult aggregatedResult) { - // TODO: Encode results using differential privacy. - return null; + /** + * Get all digests for watchlist report. + * It should include: + * (1) All installed app digests. We need this because we need to ensure after DP we don't know + * if an app is really visited C&C site. + * (2) App digests that previously recorded in database. + */ + private List<String> getAllDigestsForReport(WatchlistReportDbHelper.AggregatedResult record) { + // Step 1: Get all installed application digests. + final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications( + PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL); + final HashSet<String> result = new HashSet<>(apps.size() + record.appDigestCNCList.size()); + final int size = apps.size(); + for (int i = 0; i < size; i++) { + byte[] digest = getDigestFromUid(apps.get(i).uid); + result.add(HexDump.toHexString(digest)); + } + // Step 2: Add all digests from records + result.addAll(record.appDigestCNCList.keySet()); + return new ArrayList<>(result); } private void addEncodedReportToDropBox(byte[] encodedReport) { - final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class); - dbox.addData(DROPBOX_TAG, encodedReport, 0); + mDropBoxManager.addData(DROPBOX_TAG, encodedReport, 0); } /** * Get app digest from app uid. + * Return null if system cannot get digest from uid. */ + @Nullable private byte[] getDigestFromUid(int uid) { - final byte[] cachedDigest = mCachedUidDigestMap.get(uid); - if (cachedDigest != null) { - return cachedDigest; - } - final String[] packageNames = mPm.getPackagesForUid(uid); - final int userId = UserHandle.getUserId(uid); - if (!ArrayUtils.isEmpty(packageNames)) { - for (String packageName : packageNames) { - try { - final String apkPath = mPm.getPackageInfoAsUser(packageName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId) - .applicationInfo.publicSourceDir; - if (TextUtils.isEmpty(apkPath)) { - Slog.w(TAG, "Cannot find apkPath for " + packageName); - continue; + return mCachedUidDigestMap.computeIfAbsent(uid, key -> { + final String[] packageNames = mPm.getPackagesForUid(key); + final int userId = UserHandle.getUserId(uid); + if (!ArrayUtils.isEmpty(packageNames)) { + for (String packageName : packageNames) { + try { + final String apkPath = mPm.getPackageInfoAsUser(packageName, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId) + .applicationInfo.publicSourceDir; + if (TextUtils.isEmpty(apkPath)) { + Slog.w(TAG, "Cannot find apkPath for " + packageName); + continue; + } + return DigestUtils.getSha256Hash(new File(apkPath)); + } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) { + Slog.e(TAG, "Should not happen", e); + return null; } - final byte[] digest = DigestUtils.getSha256Hash(new File(apkPath)); - mCachedUidDigestMap.put(uid, digest); - return digest; - } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) { - Slog.e(TAG, "Should not happen", e); - return null; } + } else { + Slog.e(TAG, "Should not happen"); } - } else { - Slog.e(TAG, "Should not happen"); - } - return null; + return null; + }); } /** @@ -247,7 +278,7 @@ class WatchlistLoggingHandler extends Handler { if (ipAddr == null) { return false; } - return mSettings.containsIp(ipAddr); + return mConfig.containsIp(ipAddr); } /** Search if the host is in watchlist */ @@ -255,7 +286,7 @@ class WatchlistLoggingHandler extends Handler { if (host == null) { return false; } - return mSettings.containsDomain(host); + return mConfig.containsDomain(host); } /** diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java index 838aa53938fa..9559685c8870 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java @@ -76,13 +76,20 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { */ public static class AggregatedResult { // A list of digests that visited c&c domain or ip before. - Set<String> appDigestList; + final Set<String> appDigestList; // The c&c domain or ip visited before. - String cncDomainVisited; + final String cncDomainVisited; // A list of app digests and c&c domain visited. - HashMap<String, String> appDigestCNCList; + final HashMap<String, String> appDigestCNCList; + + public AggregatedResult(Set<String> appDigestList, String cncDomainVisited, + HashMap<String, String> appDigestCNCList) { + this.appDigestList = appDigestList; + this.cncDomainVisited = cncDomainVisited; + this.appDigestCNCList = appDigestCNCList; + } } static File getSystemWatchlistDbFile() { @@ -151,23 +158,21 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { if (c == null || c.getCount() == 0) { return null; } - final AggregatedResult result = new AggregatedResult(); - result.cncDomainVisited = null; - // After aggregation, each digest maximum will have only 1 record. - result.appDigestList = new HashSet<>(); - result.appDigestCNCList = new HashMap<>(); + final HashSet<String> appDigestList = new HashSet<>(); + final HashMap<String, String> appDigestCNCList = new HashMap<>(); + String cncDomainVisited = null; while (c.moveToNext()) { // We use hex string here as byte[] cannot be a key in HashMap. String digestHexStr = HexDump.toHexString(c.getBlob(INDEX_DIGEST)); String cncDomain = c.getString(INDEX_CNC_DOMAIN); - result.appDigestList.add(digestHexStr); - if (result.cncDomainVisited != null) { - result.cncDomainVisited = cncDomain; + appDigestList.add(digestHexStr); + if (cncDomainVisited != null) { + cncDomainVisited = cncDomain; } - result.appDigestCNCList.put(digestHexStr, cncDomain); + appDigestCNCList.put(digestHexStr, cncDomain); } - return result; + return new AggregatedResult(appDigestList, cncDomainVisited, appDigestCNCList); } finally { if (c != null) { c.close(); diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java index 70002ea21aff..b78fe4d2cca7 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 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. @@ -22,7 +22,6 @@ import android.util.Log; import android.util.Slog; import android.util.Xml; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.HexDump; @@ -35,199 +34,126 @@ import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; import java.util.zip.CRC32; /** - * A util class to do watchlist settings operations, like setting watchlist, query if a domain - * exists in watchlist. + * Class for handling watchlist settings operations, like getting differential privacy secret key. + * Unlike WatchlistConfig, which will read configs that pushed from ConfigUpdater only, this class + * can read and write all settings for watchlist operations. */ class WatchlistSettings { - private static final String TAG = "WatchlistSettings"; - // Watchlist config that pushed by ConfigUpdater. - private static final String NETWORK_WATCHLIST_DB_PATH = - "/data/misc/network_watchlist/network_watchlist.xml"; - - private static class XmlTags { - private static final String WATCHLIST_SETTINGS = "watchlist-settings"; - private static final String SHA256_DOMAIN = "sha256-domain"; - private static final String CRC32_DOMAIN = "crc32-domain"; - private static final String SHA256_IP = "sha256-ip"; - private static final String CRC32_IP = "crc32-ip"; - private static final String HASH = "hash"; - } + private static final String TAG = "WatchlistSettings"; - private static class CrcShaDigests { - final HarmfulDigests crc32Digests; - final HarmfulDigests sha256Digests; - - public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) { - this.crc32Digests = crc32Digests; - this.sha256Digests = sha256Digests; - } - } + private static final String FILE_NAME = "watchlist_settings.xml"; + // Rappor requires min entropy input size = 48 bytes + private static final int SECRET_KEY_LENGTH = 48; private final static WatchlistSettings sInstance = new WatchlistSettings(); private final AtomicFile mXmlFile; - private volatile CrcShaDigests mDomainDigests; - private volatile CrcShaDigests mIpDigests; + private byte[] mPrivacySecretKey = null; public static WatchlistSettings getInstance() { return sInstance; } private WatchlistSettings() { - this(new File(NETWORK_WATCHLIST_DB_PATH)); + this(getSystemWatchlistFile()); + } + + static File getSystemWatchlistFile() { + return new File(Environment.getDataSystemDirectory(), FILE_NAME); } @VisibleForTesting protected WatchlistSettings(File xmlFile) { mXmlFile = new AtomicFile(xmlFile); reloadSettings(); + if (mPrivacySecretKey == null) { + // Generate a new secret key and save settings + mPrivacySecretKey = generatePrivacySecretKey(); + saveSettings(); + } } public void reloadSettings() { try (FileInputStream stream = mXmlFile.openRead()){ - - final List<byte[]> crc32DomainList = new ArrayList<>(); - final List<byte[]> sha256DomainList = new ArrayList<>(); - final List<byte[]> crc32IpList = new ArrayList<>(); - final List<byte[]> sha256IpList = new ArrayList<>(); - XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); - parser.nextTag(); - parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS); - while (parser.nextTag() == XmlPullParser.START_TAG) { - String tagName = parser.getName(); - switch (tagName) { - case XmlTags.CRC32_DOMAIN: - parseHash(parser, tagName, crc32DomainList); - break; - case XmlTags.CRC32_IP: - parseHash(parser, tagName, crc32IpList); - break; - case XmlTags.SHA256_DOMAIN: - parseHash(parser, tagName, sha256DomainList); - break; - case XmlTags.SHA256_IP: - parseHash(parser, tagName, sha256IpList); - break; - default: - Log.w(TAG, "Unknown element: " + parser.getName()); - XmlUtils.skipCurrentTag(parser); + XmlUtils.beginDocument(parser, "network-watchlist-settings"); + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("secret-key")) { + mPrivacySecretKey = parseSecretKey(parser); } } - parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS); - writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList); - Log.i(TAG, "Reload watchlist done"); + Log.i(TAG, "Reload watchlist settings done"); } catch (IllegalStateException | NullPointerException | NumberFormatException | XmlPullParserException | IOException | IndexOutOfBoundsException e) { Slog.e(TAG, "Failed parsing xml", e); } } - private void parseHash(XmlPullParser parser, String tagName, List<byte[]> hashSet) + private byte[] parseSecretKey(XmlPullParser parser) throws IOException, XmlPullParserException { - parser.require(XmlPullParser.START_TAG, null, tagName); - while (parser.nextTag() == XmlPullParser.START_TAG) { - parser.require(XmlPullParser.START_TAG, null, XmlTags.HASH); - byte[] hash = HexDump.hexStringToByteArray(parser.nextText()); - parser.require(XmlPullParser.END_TAG, null, XmlTags.HASH); - hashSet.add(hash); + parser.require(XmlPullParser.START_TAG, null, "secret-key"); + byte[] key = HexDump.hexStringToByteArray(parser.nextText()); + parser.require(XmlPullParser.END_TAG, null, "secret-key"); + if (key == null || key.length != SECRET_KEY_LENGTH) { + Log.e(TAG, "Unable to parse secret key"); + return null; } - parser.require(XmlPullParser.END_TAG, null, tagName); + return key; } /** - * Write network watchlist settings to memory. + * Get DP secret key. + * Make sure it is not exported or logged in anywhere. */ - public void writeSettingsToMemory(List<byte[]> newCrc32DomainList, - List<byte[]> newSha256DomainList, - List<byte[]> newCrc32IpList, - List<byte[]> newSha256IpList) { - mDomainDigests = new CrcShaDigests(new HarmfulDigests(newCrc32DomainList), - new HarmfulDigests(newSha256DomainList)); - mIpDigests = new CrcShaDigests(new HarmfulDigests(newCrc32IpList), - new HarmfulDigests(newSha256IpList)); + synchronized byte[] getPrivacySecretKey() { + final byte[] key = new byte[SECRET_KEY_LENGTH]; + System.arraycopy(mPrivacySecretKey, 0, key, 0, SECRET_KEY_LENGTH); + return key; } - public boolean containsDomain(String domain) { - final CrcShaDigests domainDigests = mDomainDigests; - if (domainDigests == null) { - Slog.wtf(TAG, "domainDigests should not be null"); - return false; - } - // First it does a quick CRC32 check. - final byte[] crc32 = getCrc32(domain); - if (!domainDigests.crc32Digests.contains(crc32)) { - return false; - } - // Now we do a slow SHA256 check. - final byte[] sha256 = getSha256(domain); - return domainDigests.sha256Digests.contains(sha256); + private byte[] generatePrivacySecretKey() { + final byte[] key = new byte[SECRET_KEY_LENGTH]; + (new SecureRandom()).nextBytes(key); + return key; } - public boolean containsIp(String ip) { - final CrcShaDigests ipDigests = mIpDigests; - if (ipDigests == null) { - Slog.wtf(TAG, "ipDigests should not be null"); - return false; - } - // First it does a quick CRC32 check. - final byte[] crc32 = getCrc32(ip); - if (!ipDigests.crc32Digests.contains(crc32)) { - return false; + private void saveSettings() { + FileOutputStream stream; + try { + stream = mXmlFile.startWrite(); + } catch (IOException e) { + Log.w(TAG, "Failed to write display settings: " + e); + return; } - // Now we do a slow SHA256 check. - final byte[] sha256 = getSha256(ip); - return ipDigests.sha256Digests.contains(sha256); - } - - - /** Get CRC32 of a string - * - * TODO: Review if we should use CRC32 or other algorithms - */ - private byte[] getCrc32(String str) { - final CRC32 crc = new CRC32(); - crc.update(str.getBytes()); - final long tmp = crc.getValue(); - return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255), - (byte) (tmp >> 8 & 255), (byte) (tmp & 255)}; - } - - /** Get SHA256 of a string */ - private byte[] getSha256(String str) { - MessageDigest messageDigest; try { - messageDigest = MessageDigest.getInstance("SHA256"); - } catch (NoSuchAlgorithmException e) { - /* can't happen */ - return null; + XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, StandardCharsets.UTF_8.name()); + out.startDocument(null, true); + out.startTag(null, "network-watchlist-settings"); + out.startTag(null, "secret-key"); + out.text(HexDump.toHexString(mPrivacySecretKey)); + out.endTag(null, "secret-key"); + out.endTag(null, "network-watchlist-settings"); + out.endDocument(); + mXmlFile.finishWrite(stream); + } catch (IOException e) { + Log.w(TAG, "Failed to write display settings, restoring backup.", e); + mXmlFile.failWrite(stream); } - messageDigest.update(str.getBytes()); - return messageDigest.digest(); - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("Domain CRC32 digest list:"); - mDomainDigests.crc32Digests.dump(fd, pw, args); - pw.println("Domain SHA256 digest list:"); - mDomainDigests.sha256Digests.dump(fd, pw, args); - pw.println("Ip CRC32 digest list:"); - mIpDigests.crc32Digests.dump(fd, pw, args); - pw.println("Ip SHA256 digest list:"); - mIpDigests.sha256Digests.dump(fd, pw, args); } } diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index 4c0092144da9..2584187fffe6 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -160,7 +160,7 @@ public class NotificationComparator } private boolean isCall(NotificationRecord record) { - return record.getNotification().category == Notification.CATEGORY_CALL + return record.isCategory(Notification.CATEGORY_CALL) && isDefaultPhoneApp(record.sbn.getPackageName()); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index b5d284494d20..8f672b56c10c 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1002,7 +1002,7 @@ public class ZenModeHelper { if (isChange && policy.doNotDisturbWhenSilent) { if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS && mZenMode != Global.ZEN_MODE_ALARMS) { - newZen = Global.ZEN_MODE_ALARMS; + newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; } setPreviousRingerModeSetting(ringerModeOld); } @@ -1042,7 +1042,7 @@ public class ZenModeHelper { case AudioManager.RINGER_MODE_SILENT: if (isChange) { if (mZenMode == Global.ZEN_MODE_OFF) { - newZen = Global.ZEN_MODE_ALARMS; + newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; } ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE : AudioManager.RINGER_MODE_SILENT; diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java index 2a2ff06b7a5a..a6200bf433e4 100644 --- a/services/core/java/com/android/server/oemlock/OemLockService.java +++ b/services/core/java/com/android/server/oemlock/OemLockService.java @@ -31,10 +31,10 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.service.oemlock.IOemLockService; -import android.service.persistentdata.PersistentDataBlockManager; import android.util.Slog; import com.android.server.LocalServices; +import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemService; import com.android.server.pm.UserRestrictionsUtils; @@ -217,13 +217,12 @@ public class OemLockService extends SystemService { * is used to erase FRP information on a unlockable device. */ private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) { - final PersistentDataBlockManager pdbm = (PersistentDataBlockManager) - mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); + final PersistentDataBlockManagerInternal pdbmi + = LocalServices.getService(PersistentDataBlockManagerInternal.class); // if mOemLock is PersistentDataBlockLock, then the bit should have already been set - if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock) - && pdbm.getOemUnlockEnabled() != allowed) { + if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) { Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed); - pdbm.setOemUnlockEnabled(allowed); + pdbmi.forceOemUnlockEnabled(allowed); } } diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/CrossProfileAppsService.java index 0913269f35e1..027a302a325e 100644 --- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.crossprofile; +package com.android.server.pm; import android.content.Context; diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index a517d6d1a99e..2007a0e43aa1 100644 --- a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.crossprofile; +package com.android.server.pm; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; @@ -25,14 +25,12 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ICrossProfileApps; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; -import android.content.pm.crossprofile.ICrossProfileApps; -import android.graphics.Rect; import android.os.Binder; -import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index a43818a27a73..cc448daa2619 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -17,10 +17,12 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED; +import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA; import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_WRONLY; @@ -96,6 +98,7 @@ import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter; +import android.content.pm.dex.DexMetadataHelper; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -259,6 +262,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // entries like "lost+found". if (file.isDirectory()) return false; if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false; + if (DexMetadataHelper.isDexMetadataFile(file)) return false; return true; } }; @@ -939,6 +943,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mInstallerPackageName, mInstallerUid, user, mSigningDetails); } + private static void maybeRenameFile(File from, File to) throws PackageManagerException { + if (!from.equals(to)) { + if (!from.renameTo(to)) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Could not rename file " + from + " to " + to); + } + } + } + /** * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. @@ -983,6 +996,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); } + // Verify that all staged packages are internally consistent final ArraySet<String> stagedSplits = new ArraySet<>(); for (File addedFile : addedFiles) { @@ -1013,9 +1027,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Take this opportunity to enforce uniform naming final String targetName; if (apk.splitName == null) { - targetName = "base.apk"; + targetName = "base" + APK_FILE_EXTENSION; } else { - targetName = "split_" + apk.splitName + ".apk"; + targetName = "split_" + apk.splitName + APK_FILE_EXTENSION; } if (!FileUtils.isValidExtFilename(targetName)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, @@ -1023,9 +1037,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } final File targetFile = new File(mResolvedStageDir, targetName); - if (!addedFile.equals(targetFile)) { - addedFile.renameTo(targetFile); - } + maybeRenameFile(addedFile, targetFile); // Base is coming from session if (apk.splitName == null) { @@ -1033,6 +1045,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mResolvedStagedFiles.add(targetFile); + + final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile); + if (dexMetadataFile != null) { + if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Invalid filename: " + dexMetadataFile); + } + final File targetDexMetadataFile = new File(mResolvedStageDir, + DexMetadataHelper.buildDexMetadataPathForApk(targetName)); + mResolvedStagedFiles.add(targetDexMetadataFile); + maybeRenameFile(dexMetadataFile, targetDexMetadataFile); + } } if (removeSplitList.size() > 0) { @@ -1097,6 +1121,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (mResolvedBaseFile == null) { mResolvedBaseFile = new File(appInfo.getBaseCodePath()); mResolvedInheritedFiles.add(mResolvedBaseFile); + // Inherit the dex metadata if present. + final File baseDexMetadataFile = + DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile); + if (baseDexMetadataFile != null) { + mResolvedInheritedFiles.add(baseDexMetadataFile); + } } // Inherit splits if not overridden @@ -1107,6 +1137,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean splitRemoved = removeSplitList.contains(splitName); if (!stagedSplits.contains(splitName) && !splitRemoved) { mResolvedInheritedFiles.add(splitFile); + // Inherit the dex metadata if present. + final File splitDexMetadataFile = + DexMetadataHelper.findDexMetadataForFile(splitFile); + if (splitDexMetadataFile != null) { + mResolvedInheritedFiles.add(splitDexMetadataFile); + } } } } @@ -1163,43 +1199,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** - * Calculate the final install footprint size, combining both staged and - * existing APKs together and including unpacked native code from both. - */ - private long calculateInstalledSize() throws PackageManagerException { - Preconditions.checkNotNull(mResolvedBaseFile); - - final ApkLite baseApk; - try { - baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0); - } catch (PackageParserException e) { - throw PackageManagerException.from(e); - } - - final List<String> splitPaths = new ArrayList<>(); - for (File file : mResolvedStagedFiles) { - if (mResolvedBaseFile.equals(file)) continue; - splitPaths.add(file.getAbsolutePath()); - } - for (File file : mResolvedInheritedFiles) { - if (mResolvedBaseFile.equals(file)) continue; - splitPaths.add(file.getAbsolutePath()); - } - - // This is kind of hacky; we're creating a half-parsed package that is - // straddled between the inherited and staged APKs. - final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null, - splitPaths.toArray(new String[splitPaths.size()]), null); - - try { - return PackageHelper.calculateInstalledSize(pkg, params.abiOverride); - } catch (IOException e) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, - "Failed to calculate install size", e); - } - } - - /** * Determine if creating hard links between source and destination is * possible. That is, do they all live on the same underlying device. */ diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6e9452388c09..10b377d9a81e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -48,7 +48,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; -import static android.content.pm.PackageManager.INSTALL_FAILED_NEWER_SDK; import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE; @@ -191,6 +190,7 @@ import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.content.pm.VersionedPackage; +import android.content.pm.dex.DexMetadataHelper; import android.content.pm.dex.IArtManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -10764,6 +10764,26 @@ Slog.e("TODD", } } } + + // Verify that packages sharing a user with a privileged app are marked as privileged. + if (!pkg.isPrivileged() && (pkg.mSharedUserId != null)) { + SharedUserSetting sharedUserSetting = null; + try { + sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, false); + } catch (PackageManagerException ignore) {} + if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) { + // Exempt SharedUsers signed with the platform key. + PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); + if ((platformPkgSetting.signatures.mSignatures != null) && + (compareSignatures(platformPkgSetting.signatures.mSignatures, + pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) { + throw new PackageManagerException("Apps that share a user with a " + + "privileged app must themselves be marked as privileged. " + + pkg.packageName + " shares privileged user " + + pkg.mSharedUserId + "."); + } + } + } } } @@ -16466,6 +16486,7 @@ Slog.e("TODD", final PackageParser.Package pkg; try { pkg = pp.parsePackage(tmpPackageFile, parseFlags); + DexMetadataHelper.validatePackageDexMetadata(pkg); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; @@ -16473,16 +16494,6 @@ Slog.e("TODD", Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - // App targetSdkVersion is below min supported version - if (!forceSdk && pkg.applicationInfo.isTargetingDeprecatedSdkVersion()) { - Slog.w(TAG, "App " + pkg.packageName + " targets deprecated sdk"); - - res.setError(INSTALL_FAILED_NEWER_SDK, - "App is targeting deprecated sdk (targetSdkVersion should be at least " - + Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT + ")."); - return; - } - // Instant apps have several additional install-time checks. if (instantApp) { if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) { @@ -18878,6 +18889,14 @@ Slog.e("TODD", return Build.VERSION_CODES.CUR_DEVELOPMENT; } + private int getPackageTargetSdkVersionLockedLPr(String packageName) { + final PackageParser.Package p = mPackages.get(packageName); + if (p != null) { + return p.applicationInfo.targetSdkVersion; + } + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + @Override public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { @@ -23397,6 +23416,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public int getPackageTargetSdkVersion(String packageName) { + synchronized (mPackages) { + return getPackageTargetSdkVersionLockedLPr(packageName); + } + } + + @Override public boolean canAccessInstantApps(int callingUid, int userId) { return PackageManagerService.this.canViewInstantApps(callingUid, userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index bd1cd3308ea7..a33f0716c4cb 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -18,6 +18,7 @@ package com.android.server.pm; import android.accounts.IAccountManager; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; @@ -46,6 +47,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; +import android.content.pm.dex.DexMetadataHelper; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; @@ -72,13 +74,13 @@ import android.util.PrintWriterPrinter; import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.SizedInputStream; +import com.android.server.LocalServices; import com.android.server.SystemConfig; import dalvik.system.DexFile; import libcore.io.IoUtils; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -222,6 +224,8 @@ class PackageManagerShellCommand extends ShellCommand { return runSetUserRestriction(); case "get-max-users": return runGetMaxUsers(); + case "get-max-running-users": + return runGetMaxRunningUsers(); case "set-home-activity": return runSetHomeActivity(); case "set-installer": @@ -1883,6 +1887,14 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + public int runGetMaxRunningUsers() { + ActivityManagerInternal activityManagerInternal = + LocalServices.getService(ActivityManagerInternal.class); + getOutPrintWriter().println("Maximum supported running users: " + + activityManagerInternal.getMaxRunningUsers()); + return 0; + } + private static class InstallParams { SessionParams sessionParams; String installerPackageName; @@ -2246,6 +2258,14 @@ class PackageManagerShellCommand extends ShellCommand { session = new PackageInstaller.Session( mInterface.getPackageInstaller().openSession(sessionId)); + // Sanity check that all .dm files match an apk. + // (The installer does not support standalone .dm files and will not process them.) + try { + DexMetadataHelper.validateDexPaths(session.getNames()); + } catch (IllegalStateException | IOException e) { + pw.println("Warning [Could not validate the dex paths: " + e.getMessage() + "]"); + } + final LocalIntentReceiver receiver = new LocalIntentReceiver(); session.commit(receiver.getIntentSender()); @@ -2607,6 +2627,8 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" get-max-users"); pw.println(""); + pw.println(" get-max-running-users"); + pw.println(""); pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]"); pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)"); pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\". Options are:"); diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index 877da144730f..244613180d00 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -17,6 +17,7 @@ package com.android.server.pm; import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; import android.service.pm.PackageServiceDumpProto; import android.util.ArraySet; @@ -102,4 +103,8 @@ public final class SharedUserSetting extends SettingBase { } return pkgList; } + + public boolean isPrivileged() { + return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index a453c333d560..6599fd94de3a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -66,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; @@ -163,13 +162,10 @@ import android.app.StatusBarManager; import android.app.UiModeManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; -import android.content.ComponentCallbacks; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -202,7 +198,6 @@ import android.os.IBinder; import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; -import android.os.Messenger; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; @@ -850,7 +845,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj); break; case MSG_DISPATCH_SHOW_RECENTS: - showRecentApps(false, msg.arg1 != 0); + showRecentApps(false); break; case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS: showGlobalActionsInternal(); @@ -1030,8 +1025,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void run() { // send interaction hint to improve redraw performance mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0); - if (showRotationChoice(mCurrentAppOrientation, mRotation)) { - sendProposedRotationChangeToStatusBarInternal(mRotation); + if (isRotationChoiceEnabled()) { + final boolean isValid = isValidRotationChoice(mCurrentAppOrientation, + mRotation); + sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); } else { updateRotation(false); } @@ -3817,7 +3814,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { mRecentAppsHeldModifiers = shiftlessModifiers; - showRecentApps(true, false); + showRecentApps(true); return -1; } } @@ -4164,16 +4161,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void showRecentApps(boolean fromHome) { + public void showRecentApps() { mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS); - mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS, fromHome ? 1 : 0, 0).sendToTarget(); + mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget(); } - private void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { + private void showRecentApps(boolean triggeredFromAltTab) { mPreloadedRecentApps = false; // preloading no longer needs to be canceled StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); if (statusbar != null) { - statusbar.showRecentApps(triggeredFromAltTab, fromHome); + statusbar.showRecentApps(triggeredFromAltTab); } } @@ -4498,7 +4495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); mInputConsumer = null; } - } else if (mInputConsumer == null) { + } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) { mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(), INPUT_CONSUMER_NAVIGATION, (channel, looper) -> new HideNavInputEventReceiver(channel, looper)); @@ -5655,11 +5652,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean allowAppAnimationsLw() { - if (mShowingDream) { - // If keyguard or dreams is currently visible, no reason to animate behind it. - return false; - } - return true; + return !mShowingDream; } @Override @@ -6197,10 +6190,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** * Notify the StatusBar that system rotation suggestion has changed. */ - private void sendProposedRotationChangeToStatusBarInternal(int rotation) { + private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) { StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); if (statusBar != null) { - statusBar.onProposedRotationChanged(rotation); + statusBar.onProposedRotationChanged(rotation, isValid); } } @@ -7146,15 +7139,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mOrientationListener.setCurrentRotation(rotation); } - public boolean showRotationChoice(int orientation, final int preferredRotation) { + public boolean isRotationChoiceEnabled() { // Rotation choice is only shown when the user is in locked mode. if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false; - // We should only show a rotation choice if: - // 1. The rotation isn't forced by the lid, dock, demo, hdmi, vr, etc mode - // 2. The user choice won't be ignored due to screen orientation settings + // We should only enable rotation choice if the rotation isn't forced by the lid, dock, + // demo, hdmi, vr, etc mode - // Determine if the rotation currently forced + // Determine if the rotation is currently forced if (mForceDefaultOrientation) { return false; // Rotation is forced to default orientation @@ -7187,7 +7179,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } - // Determine if the orientation will ignore user choice + // Rotation isn't forced, enable choice + return true; + } + + public boolean isValidRotationChoice(int orientation, final int preferredRotation) { + // Determine if the given app orientation can be chosen and, if so, if it is compatible + // with the provided rotation choice + switch (orientation) { case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 60dae534547e..e9c4c5c8138f 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1206,13 +1206,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Return true if it is okay to perform animations for an app transition - * that is about to occur. You may return false for this if, for example, - * the lock screen is currently displayed so the switch should happen + * that is about to occur. You may return false for this if, for example, + * the dream window is currently displayed so the switch should happen * immediately. */ public boolean allowAppAnimationsLw(); - /** * A new window has been focused. */ @@ -1572,7 +1571,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Show the recents task list app. * @hide */ - public void showRecentApps(boolean fromHome); + public void showRecentApps(); /** * Show the global actions dialog. diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java index cf930f5cc6b7..09f6da92939c 100644 --- a/services/core/java/com/android/server/slice/PinnedSliceState.java +++ b/services/core/java/com/android/server/slice/PinnedSliceState.java @@ -22,6 +22,7 @@ import android.content.ContentProviderClient; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -51,6 +52,8 @@ public class PinnedSliceState { private final ArraySet<ISliceListener> mListeners = new ArraySet<>(); @GuardedBy("mLock") private SliceSpec[] mSupportedSpecs = null; + @GuardedBy("mLock") + private final ArrayMap<ISliceListener, String> mPkgMap = new ArrayMap<>(); public PinnedSliceState(SliceManagerService service, Uri uri) { mService = service; @@ -102,17 +105,19 @@ public class PinnedSliceState { mService.getHandler().post(this::handleBind); } - public void addSliceListener(ISliceListener listener, SliceSpec[] specs) { + public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) { synchronized (mLock) { if (mListeners.add(listener) && mListeners.size() == 1) { mService.listen(mUri); } + mPkgMap.put(listener, pkg); mergeSpecs(specs); } } public boolean removeSliceListener(ISliceListener listener) { synchronized (mLock) { + mPkgMap.remove(listener); if (mListeners.remove(listener) && mListeners.size() == 0) { mService.unlisten(mUri); } @@ -155,25 +160,16 @@ public class PinnedSliceState { } private void handleBind() { - Slice s; - try (ContentProviderClient client = getClient()) { - Bundle extras = new Bundle(); - extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); - extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, - new ArrayList<>(Arrays.asList(mSupportedSpecs))); - final Bundle res; - try { - res = client.call(SliceProvider.METHOD_SLICE, null, extras); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind slice " + mUri, e); - return; - } - if (res == null) return; - Bundle.setDefusable(res, true); - s = res.getParcelable(SliceProvider.EXTRA_SLICE); - } + Slice cachedSlice = doBind(null); synchronized (mLock) { mListeners.removeIf(l -> { + Slice s = cachedSlice; + if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) { + s = doBind(mPkgMap.get(l)); + } + if (s == null) { + return true; + } try { l.onSliceUpdated(s); return false; @@ -189,6 +185,26 @@ public class PinnedSliceState { } } + private Slice doBind(String overridePkg) { + try (ContentProviderClient client = getClient()) { + Bundle extras = new Bundle(); + extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); + extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, + new ArrayList<>(Arrays.asList(mSupportedSpecs))); + extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, overridePkg); + final Bundle res; + try { + res = client.call(SliceProvider.METHOD_SLICE, null, extras); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind slice " + mUri, e); + return null; + } + if (res == null) return null; + Bundle.setDefusable(res, true); + return res.getParcelable(SliceProvider.EXTRA_SLICE); + } + } + private void handleSendPinned() { try (ContentProviderClient client = getClient()) { Bundle b = new Bundle(); diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 2d9e772a6b0c..c62a8560be20 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -16,27 +16,35 @@ package com.android.server.slice; +import static android.content.ContentProvider.getUriWithoutUserId; import static android.content.ContentProvider.getUserIdFromUri; import static android.content.ContentProvider.maybeAddUserId; import android.Manifest.permission; +import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.ContentProviderHolder; +import android.app.IActivityManager; import android.app.slice.ISliceListener; import android.app.slice.ISliceManager; +import android.app.slice.SliceManager; import android.app.slice.SliceSpec; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -63,6 +71,8 @@ public class SliceManagerService extends ISliceManager.Stub { @GuardedBy("mLock") private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>(); + @GuardedBy("mLock") + private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>(); private final Handler mHandler; private final ContentObserver mObserver; @@ -111,7 +121,7 @@ public class SliceManagerService extends ISliceManager.Stub { verifyCaller(pkg); uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier()); enforceAccess(pkg, uri); - getOrCreatePinnedSlice(uri).addSliceListener(listener, specs); + getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs); } @Override @@ -156,6 +166,43 @@ public class SliceManagerService extends ISliceManager.Stub { return getPinnedSlice(uri).getSpecs(); } + @Override + public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException { + if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + == PackageManager.PERMISSION_GRANTED) { + return SliceManager.PERMISSION_GRANTED; + } + if (hasFullSliceAccess(pkg, uid)) { + return SliceManager.PERMISSION_GRANTED; + } + synchronized (mLock) { + if (mUserGrants.contains(new SliceGrant(uri, pkg))) { + return SliceManager.PERMISSION_USER_GRANTED; + } + } + return SliceManager.PERMISSION_DENIED; + } + + @Override + public void grantPermissionFromUser(Uri uri, String pkg, String callingPkg, boolean allSlices) { + verifyCaller(callingPkg); + getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS, + "Slice granting requires MANAGE_SLICE_PERMISSIONS"); + if (allSlices) { + // TODO: Manage full access grants. + } else { + synchronized (mLock) { + mUserGrants.add(new SliceGrant(uri, pkg)); + } + long ident = Binder.clearCallingIdentity(); + try { + mContext.getContentResolver().notifyChange(uri, null); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + /// ----- internal code ----- void removePinnedSlice(Uri uri) { synchronized (mLock) { @@ -186,7 +233,7 @@ public class SliceManagerService extends ISliceManager.Stub { } @VisibleForTesting - PinnedSliceState createPinnedSlice(Uri uri) { + protected PinnedSliceState createPinnedSlice(Uri uri) { return new PinnedSliceState(this, uri); } @@ -202,12 +249,45 @@ public class SliceManagerService extends ISliceManager.Stub { return mHandler; } - private void enforceAccess(String pkg, Uri uri) { - getContext().enforceUriPermission(uri, permission.BIND_SLICE, - permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(), - Intent.FLAG_GRANT_WRITE_URI_PERMISSION, - "Slice binding requires the permission BIND_SLICE"); + private void enforceAccess(String pkg, Uri uri) throws RemoteException { int user = Binder.getCallingUserHandle().getIdentifier(); + // Check for default launcher/assistant. + if (!hasFullSliceAccess(pkg, Binder.getCallingUid())) { + try { + // Also allow things with uri access. + getContext().enforceUriPermission(uri, Binder.getCallingPid(), + Binder.getCallingUid(), + Intent.FLAG_GRANT_WRITE_URI_PERMISSION, + "Slice binding requires permission to the Uri"); + } catch (SecurityException e) { + // Last fallback (if the calling app owns the authority, then it can have access). + long ident = Binder.clearCallingIdentity(); + try { + IBinder token = new Binder(); + IActivityManager activityManager = ActivityManager.getService(); + ContentProviderHolder holder = null; + String providerName = getUriWithoutUserId(uri).getAuthority(); + try { + holder = activityManager.getContentProviderExternal( + providerName, getUserIdFromUri(uri, user), token); + if (holder == null || holder.info == null + || !Objects.equals(holder.info.packageName, pkg)) { + // No more fallbacks, no access. + throw e; + } + } finally { + if (holder != null && holder.provider != null) { + activityManager.removeContentProviderExternal(providerName, token); + } + } + } finally { + // I know, the double finally seems ugly, but seems safest for the identity. + Binder.restoreCallingIdentity(ident); + } + } + } + // Lastly check for any multi-userness. Any return statements above here will break this + // important check. if (getUserIdFromUri(uri, user) != user) { getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL, "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL"); @@ -230,8 +310,14 @@ public class SliceManagerService extends ISliceManager.Stub { } private boolean hasFullSliceAccess(String pkg, int userId) { - return isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) - || isGrantedFullAccess(pkg, userId); + long ident = Binder.clearCallingIdentity(); + try { + boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId) + || isGrantedFullAccess(pkg, userId); + return ret; + } finally { + Binder.restoreCallingIdentity(ident); + } } private boolean isAssistant(String pkg, int userId) { @@ -259,13 +345,14 @@ public class SliceManagerService extends ISliceManager.Stub { private boolean isDefaultHomeApp(String pkg, int userId) { String defaultHome = getDefaultHome(userId); - return Objects.equals(pkg, defaultHome); + + return pkg != null && Objects.equals(pkg, defaultHome); } // Based on getDefaultHome in ShortcutService. // TODO: Unify if possible @VisibleForTesting - String getDefaultHome(int userId) { + protected String getDefaultHome(int userId) { final long token = Binder.clearCallingIdentity(); try { final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); @@ -301,7 +388,7 @@ public class SliceManagerService extends ISliceManager.Stub { lastPriority = ri.priority; } } - return detected.getPackageName(); + return detected != null ? detected.getPackageName() : null; } finally { Binder.restoreCallingIdentity(token); } @@ -349,4 +436,26 @@ public class SliceManagerService extends ISliceManager.Stub { mService.onStopUser(userHandle); } } + + private class SliceGrant { + private final Uri mUri; + private final String mPkg; + + public SliceGrant(Uri uri, String pkg) { + mUri = uri; + mPkg = pkg; + } + + @Override + public int hashCode() { + return mUri.hashCode() + mPkg.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SliceGrant)) return false; + SliceGrant other = (SliceGrant) obj; + return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg); + } + } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 2f5e2f8665f9..4cb5e089bd83 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -28,6 +28,7 @@ import android.content.pm.UserInfo; import android.net.NetworkStats; import android.net.wifi.IWifiManager; import android.net.wifi.WifiActivityEnergyInfo; +import android.os.SystemClock; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.os.BatteryStatsInternal; @@ -621,6 +622,20 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } break; } + case StatsLog.CPU_SUSPEND_TIME: { + List<StatsLogEventWrapper> ret = new ArrayList(); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1); + e.writeLong(SystemClock.elapsedRealtime()); + ret.add(e); + break; + } + case StatsLog.CPU_IDLE_TIME: { + List<StatsLogEventWrapper> ret = new ArrayList(); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1); + e.writeLong(SystemClock.uptimeMillis()); + ret.add(e); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 3792bc6777ed..95006ffb2ed6 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -30,7 +30,7 @@ public interface StatusBarManagerInternal { void cancelPreloadRecentApps(); - void showRecentApps(boolean triggeredFromAltTab, boolean fromHome); + void showRecentApps(boolean triggeredFromAltTab); void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey); @@ -95,7 +95,7 @@ public interface StatusBarManagerInternal { * * @param rotation rotation suggestion */ - void onProposedRotationChanged(int rotation); + void onProposedRotationChanged(int rotation, boolean isValid); public interface GlobalActionsListener { /** diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index c7c03b48a778..c58c208dea81 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -282,10 +282,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { + public void showRecentApps(boolean triggeredFromAltTab) { if (mBar != null) { try { - mBar.showRecentApps(triggeredFromAltTab, fromHome); + mBar.showRecentApps(triggeredFromAltTab); } catch (RemoteException ex) {} } } @@ -408,10 +408,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void onProposedRotationChanged(int rotation) { + public void onProposedRotationChanged(int rotation, boolean isValid) { if (mBar != null){ try { - mBar.onProposedRotationChanged(rotation); + mBar.onProposedRotationChanged(rotation, isValid); } catch (RemoteException ex) {} } } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index de723c6701d3..d84fbc53c5f9 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -185,6 +185,14 @@ public class VrManagerService extends SystemService ComponentName component = null; synchronized (mLock) { component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent()); + + // If the VrCore main service was disconnected or the binding died we'll rebind + // automatically. Call focusedActivityChanged() once we rebind. + if (component != null && component.equals(event.component) && + (event.event == LogEvent.EVENT_DISCONNECTED || + event.event == LogEvent.EVENT_BINDING_DIED)) { + callFocusedActivityChangedLocked(); + } } // If not on an AIO device and we permanently stopped trying to connect to the @@ -980,16 +988,7 @@ public class VrManagerService extends SystemService oldVrServicePackage, oldUserId); if (mCurrentVrService != null && sendUpdatedCaller) { - final ComponentName c = mCurrentVrModeComponent; - final boolean b = running2dInVr; - final int pid = processId; - mCurrentVrService.sendEvent(new PendingEvent() { - @Override - public void runEvent(IInterface service) throws RemoteException { - IVrListener l = (IVrListener) service; - l.focusedActivityChanged(c, b, pid); - } - }); + callFocusedActivityChangedLocked(); } if (!nothingChanged) { @@ -1002,6 +1001,23 @@ public class VrManagerService extends SystemService } } + private void callFocusedActivityChangedLocked() { + final ComponentName c = mCurrentVrModeComponent; + final boolean b = mRunning2dInVr; + final int pid = mVrAppProcessId; + mCurrentVrService.sendEvent(new PendingEvent() { + @Override + public void runEvent(IInterface service) throws RemoteException { + // Under specific (and unlikely) timing scenarios, when VrCore + // crashes and is rebound, focusedActivityChanged() may be + // called a 2nd time with the same arguments. IVrListeners + // should make sure to handle that scenario gracefully. + IVrListener l = (IVrListener) service; + l.focusedActivityChanged(c, b, pid); + } + }); + } + private boolean isDefaultAllowed(String packageName) { PackageManager pm = mContext.getPackageManager(); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 163b1600e0bd..659253f9d603 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -346,12 +346,12 @@ final class AccessibilityController { final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); if (magnifying) { switch (transition) { - case AppTransition.TRANSIT_ACTIVITY_OPEN: - case AppTransition.TRANSIT_TASK_OPEN: - case AppTransition.TRANSIT_TASK_TO_FRONT: - case AppTransition.TRANSIT_WALLPAPER_OPEN: - case AppTransition.TRANSIT_WALLPAPER_CLOSE: - case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: { + case WindowManager.TRANSIT_ACTIVITY_OPEN: + case WindowManager.TRANSIT_TASK_OPEN: + case WindowManager.TRANSIT_TASK_TO_FRONT: + case WindowManager.TRANSIT_WALLPAPER_OPEN: + case WindowManager.TRANSIT_WALLPAPER_CLOSE: + case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: { mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); } } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index f129fe161f0a..fc7ad09d5182 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -16,6 +16,28 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_UNSET; +import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; +import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation; @@ -78,7 +100,8 @@ import android.view.IAppTransitionAnimationSpecsFuture; import android.view.RemoteAnimationAdapter; import android.view.RenderNode; import android.view.ThreadedRenderer; -import android.view.WindowManager; +import android.view.WindowManager.TransitionFlags; +import android.view.WindowManager.TransitionType; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; @@ -110,63 +133,6 @@ public class AppTransition implements Dump { private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM; private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8; - /** Not set up for a transition. */ - public static final int TRANSIT_UNSET = -1; - /** No animation for transition. */ - public static final int TRANSIT_NONE = 0; - /** A window in a new activity is being opened on top of an existing one in the same task. */ - public static final int TRANSIT_ACTIVITY_OPEN = 6; - /** The window in the top-most activity is being closed to reveal the - * previous activity in the same task. */ - public static final int TRANSIT_ACTIVITY_CLOSE = 7; - /** A window in a new task is being opened on top of an existing one - * in another activity's task. */ - public static final int TRANSIT_TASK_OPEN = 8; - /** A window in the top-most activity is being closed to reveal the - * previous activity in a different task. */ - public static final int TRANSIT_TASK_CLOSE = 9; - /** A window in an existing task is being displayed on top of an existing one - * in another activity's task. */ - public static final int TRANSIT_TASK_TO_FRONT = 10; - /** A window in an existing task is being put below all other tasks. */ - public static final int TRANSIT_TASK_TO_BACK = 11; - /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that - * does, effectively closing the wallpaper. */ - public static final int TRANSIT_WALLPAPER_CLOSE = 12; - /** A window in a new activity that does have a wallpaper is being opened on one that didn't, - * effectively opening the wallpaper. */ - public static final int TRANSIT_WALLPAPER_OPEN = 13; - /** A window in a new activity is being opened on top of an existing one, and both are on top - * of the wallpaper. */ - public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14; - /** The window in the top-most activity is being closed to reveal the previous activity, and - * both are on top of the wallpaper. */ - public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15; - /** A window in a new task is being opened behind an existing one in another activity's task. - * The new window will show briefly and then be gone. */ - public static final int TRANSIT_TASK_OPEN_BEHIND = 16; - /** A window in a task is being animated in-place. */ - public static final int TRANSIT_TASK_IN_PLACE = 17; - /** An activity is being relaunched (e.g. due to configuration change). */ - public static final int TRANSIT_ACTIVITY_RELAUNCH = 18; - /** A task is being docked from recents. */ - public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19; - /** Keyguard is going away */ - public static final int TRANSIT_KEYGUARD_GOING_AWAY = 20; - /** Keyguard is going away with showing an activity behind that requests wallpaper */ - public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21; - /** Keyguard is being occluded */ - public static final int TRANSIT_KEYGUARD_OCCLUDE = 22; - /** Keyguard is being unoccluded */ - public static final int TRANSIT_KEYGUARD_UNOCCLUDE = 23; - - /** Transition flag: Keyguard is going away, but keeping the notification shade open */ - public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE = 0x1; - /** Transition flag: Keyguard is going away, but doesn't want an animation for it */ - public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION = 0x2; - /** Transition flag: Keyguard is going away while it was showing the system wallpaper. */ - public static final int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER = 0x4; - /** Fraction of animation at which the recents thumbnail stays completely transparent */ private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f; /** Fraction of animation at which the recents thumbnail becomes completely transparent */ @@ -192,8 +158,8 @@ public class AppTransition implements Dump { private final Context mContext; private final WindowManagerService mService; - private int mNextAppTransition = TRANSIT_UNSET; - private int mNextAppTransitionFlags = 0; + private @TransitionType int mNextAppTransition = TRANSIT_UNSET; + private @TransitionFlags int mNextAppTransitionFlags = 0; private int mLastUsedAppTransition = TRANSIT_UNSET; private String mLastOpeningApp; private String mLastClosingApp; @@ -326,11 +292,11 @@ public class AppTransition implements Dump { return mNextAppTransition != TRANSIT_UNSET; } - boolean isTransitionEqual(int transit) { + boolean isTransitionEqual(@TransitionType int transit) { return mNextAppTransition == transit; } - int getAppTransition() { + @TransitionType int getAppTransition() { return mNextAppTransition; } @@ -485,7 +451,7 @@ public class AppTransition implements Dump { void freeze() { final int transit = mNextAppTransition; - setAppTransition(AppTransition.TRANSIT_UNSET, 0 /* flags */); + setAppTransition(TRANSIT_UNSET, 0 /* flags */); clear(); setReady(); notifyAppTransitionCancelledLocked(transit); @@ -540,7 +506,7 @@ public class AppTransition implements Dump { return redoLayout; } - private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) { + private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg=" + (lp != null ? lp.packageName : null) + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null)); @@ -576,7 +542,7 @@ public class AppTransition implements Dump { return null; } - Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) { + Animation loadAnimationAttr(LayoutParams lp, int animAttr) { int anim = 0; Context context = mContext; if (animAttr >= 0) { @@ -592,7 +558,7 @@ public class AppTransition implements Dump { return null; } - Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) { + Animation loadAnimationRes(LayoutParams lp, int resId) { Context context = mContext; if (resId >= 0) { AttributeCache.Entry ent = getCachedAnimations(lp); @@ -1585,7 +1551,7 @@ public class AppTransition implements Dump { * to the recents thumbnail and hence need to account for the surface being * bigger. */ - Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode, + Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode, int orientation, Rect frame, Rect displayFrame, Rect insets, @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, boolean freeform, int taskId) { @@ -2157,8 +2123,8 @@ public class AppTransition implements Dump { * @return true if transition is not running and should not be skipped, false if transition is * already running */ - boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent, int flags, - boolean forceOverride) { + boolean prepareAppTransitionLocked(@TransitionType int transit, boolean alwaysKeepCurrent, + @TransitionFlags int flags, boolean forceOverride) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:" + " transit=" + appTransitionToString(transit) + " " + this diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index ae9f28bcb6a6..e9efd4ec9e3d 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -19,8 +19,8 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; +import static android.view.WindowManager.TRANSIT_UNSET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; @@ -32,7 +32,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.app.ActivityManager.TaskSnapshot; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IBinder; @@ -40,6 +39,8 @@ import android.os.Looper; import android.os.Message; import android.util.Slog; import android.view.IApplicationToken; +import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.server.AttributeCache; @@ -393,7 +394,7 @@ public class AppWindowContainerController wtoken.mEnteringAnimation = false; } if (mService.mAppTransition.getAppTransition() - == AppTransition.TRANSIT_TASK_OPEN_BEHIND) { + == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { // We're launchingBehind, add the launching activity to mOpeningApps. final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow(); @@ -695,6 +696,17 @@ public class AppWindowContainerController } } + public void registerRemoteAnimations(RemoteAnimationDefinition definition) { + synchronized (mWindowMap) { + if (mContainer == null) { + Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" + + " token: " + mToken); + return; + } + mContainer.registerRemoteAnimations(definition); + } + } + void reportStartingWindowDrawn() { mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN)); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index ed39159ca5c5..ce3f512deccf 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -34,7 +34,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static android.view.WindowManager.TRANSIT_UNSET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -91,6 +91,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.IApplicationToken; +import android.view.RemoteAnimationDefinition; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowManager; @@ -106,7 +107,6 @@ import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.LinkedList; class AppTokenList extends ArrayList<AppWindowToken> { } @@ -250,6 +250,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree private final Point mTmpPoint = new Point(); private final Rect mTmpRect = new Rect(); + private RemoteAnimationDefinition mRemoteAnimationDefinition; AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, @@ -393,7 +394,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean runningAppAnimation = false; - if (transit != AppTransition.TRANSIT_UNSET) { + if (transit != WindowManager.TRANSIT_UNSET) { if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) { delayed = runningAppAnimation = true; } @@ -462,10 +463,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token); } - // Update the client visibility if we are not running an animation. Otherwise, we'll - // update client visibility state in onAnimationFinished. - if (!visible && !delayed) { - setClientHidden(true); + // If we're becoming visible, immediately change client visibility as well although it + // usually gets changed in AppWindowContainerController.setVisibility already. However, + // there seem to be some edge cases where we change our visibility but client visibility + // never gets updated. + // If we're becoming invisible, update the client visibility if we are not running an + // animation. Otherwise, we'll update client visibility in onAnimationFinished. + if (visible || !delayed) { + setClientHidden(!visible); } // If we are hidden but there is no delay needed we immediately @@ -516,15 +521,25 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } WindowState findMainWindow() { + return findMainWindow(true); + } + + /** + * Finds the main window that either has type base application or application starting if + * requested. + * + * @param includeStartingApp Allow to search application-starting windows to also be returned. + * @return The main window of type base application or application starting if requested. + */ + WindowState findMainWindow(boolean includeStartingApp) { WindowState candidate = null; - int j = mChildren.size(); - while (j > 0) { - j--; + for (int j = mChildren.size() - 1; j >= 0; --j) { final WindowState win = mChildren.get(j); final int type = win.mAttrs.type; // No need to loop through child window as base application and starting types can't be // child windows. - if (type == TYPE_BASE_APPLICATION || type == TYPE_APPLICATION_STARTING) { + if (type == TYPE_BASE_APPLICATION + || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) { // In cases where there are multiple windows, we prefer the non-exiting window. This // happens for example when replacing windows during an activity relaunch. When // constructing the animation, we want the new window, not the exiting one. @@ -1372,8 +1387,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mLastTransactionSequence != mService.mTransactionSequence) { mLastTransactionSequence = mService.mTransactionSequence; - mNumInterestingWindows = mNumDrawnWindows = 0; + mNumDrawnWindows = 0; startingDisplayed = false; + + // There is the main base application window, even if it is exiting, wait for it + mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0; } final WindowStateAnimator winAnimator = w.mWinAnimator; @@ -1395,7 +1413,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (w != startingWindow) { if (w.isInteresting()) { - mNumInterestingWindows++; + // Add non-main window as interesting since the main app has already been added + if (findMainWindow(false /* includeStartingApp */) != w) { + mNumInterestingWindows++; + } if (w.isDrawnLw()) { mNumDrawnWindows++; @@ -1900,6 +1921,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mThumbnail = null; } + void registerRemoteAnimations(RemoteAnimationDefinition definition) { + mRemoteAnimationDefinition = definition; + } + + RemoteAnimationDefinition getRemoteAnimationDefinition() { + return mRemoteAnimationDefinition; + } + @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a8e00dd53526..6dc384a8831e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -49,6 +49,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -63,7 +64,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; @@ -134,6 +135,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.MutableBoolean; import android.util.Slog; @@ -330,6 +332,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final PinnedStackController mPinnedStackControllerLocked; final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>(); + /** A collection of windows that provide tap exclude regions inside of them. */ + final ArraySet<WindowState> mTapExcludeProvidingWindows = new ArraySet<>(); private boolean mHaveBootMsg = false; private boolean mHaveApp = false; @@ -1866,10 +1870,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) { - WindowState win = mTapExcludedWindows.get(i); + final WindowState win = mTapExcludedWindows.get(i); win.getTouchableRegion(mTmpRegion); mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); } + for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) { + final WindowState win = mTapExcludeProvidingWindows.valueAt(i); + win.amendTapExcludeRegion(mTouchExcludeRegion); + } // TODO(multi-display): Support docked stacks on secondary displays. if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) { mDividerControllerLocked.getTouchRegion(mTmpRect); @@ -3173,6 +3181,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ SurfaceControl mAppAnimationLayer = null; + /** + * Given that the split-screen divider does not have an AppWindowToken, it + * will have to live inside of a "NonAppWindowContainer", in particular + * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order + * it will need to be interleaved with some of our children, appearing on top of + * both docked stacks but underneath any assistant stacks. + * + * To solve this problem we have this anchor control, which will always exist so + * we can always assign it the correct value in our {@link #assignChildLayers}. + * Likewise since it always exists, {@link AboveAppWindowContainers} can always + * assign the divider a layer relative to it. This way we prevent linking lifecycle + * events between the two containers. + */ + SurfaceControl mSplitScreenDividerAnchor = null; + // Cached reference to some special stacks we tend to get a lot so we don't need to loop // through the list to find them. private TaskStack mHomeStack = null; @@ -3489,37 +3512,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - int layer = 0; - // We allow stacks to change visual order from the AM specified order due to - // Z-boosting during animations. However we must take care to ensure TaskStacks - // which are marked as alwaysOnTop remain that way. - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - s.assignChildLayers(); - if (!s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + final int HOME_STACK_STATE = 0; + final int NORMAL_STACK_STATE = 1; + final int ALWAYS_ON_TOP_STATE = 2; + + int layer = 0; + for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + if (state == HOME_STACK_STATE && s.isActivityTypeHome()) { + s.assignLayer(t, layer++); + } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome() + && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) { + t.setLayer(mSplitScreenDividerAnchor, layer++); + } + } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) { + s.assignLayer(t, layer++); + } } - } - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - if (s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + // The appropriate place for App-Transitions to occur is right + // above all other animations but still below things in the Picture-and-Picture + // windowing mode. + if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) { + t.setLayer(mAppAnimationLayer, layer++); } } for (int i = 0; i < mChildren.size(); i++) { final TaskStack s = mChildren.get(i); - if (s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); - } + s.assignChildLayers(t); } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (mAppAnimationLayer != null) { - t.setLayer(mAppAnimationLayer, layer++); - } } @Override @@ -3527,6 +3552,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mAppAnimationLayer; } + SurfaceControl getSplitScreenDividerAnchor() { + return mSplitScreenDividerAnchor; + } + @Override void onParentSet() { super.onParentSet(); @@ -3534,11 +3563,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAppAnimationLayer = makeChildSurface(null) .setName("animationLayer") .build(); - getPendingTransaction().show(mAppAnimationLayer); + mSplitScreenDividerAnchor = makeChildSurface(null) + .setName("splitScreenDividerAnchor") + .build(); + getPendingTransaction() + .show(mAppAnimationLayer) + .show(mSplitScreenDividerAnchor); scheduleAnimation(); } else { mAppAnimationLayer.destroy(); mAppAnimationLayer = null; + mSplitScreenDividerAnchor.destroy(); + mSplitScreenDividerAnchor = null; } } } @@ -3553,6 +3589,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && imeContainer.getSurfaceControl() != null; for (int j = 0; j < mChildren.size(); ++j) { final WindowToken wt = mChildren.get(j); + + // See {@link mSplitScreenDividerAnchor} + if (wt.windowType == TYPE_DOCK_DIVIDER) { + wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1); + continue; + } wt.assignLayer(t, j); wt.assignChildLayers(t); @@ -3690,6 +3732,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .setParent(mOverlayLayer); } + /** + * Reparents the given surface to mOverlayLayer. + */ + void reparentToOverlay(Transaction transaction, SurfaceControl surface) { + transaction.reparent(surface, mOverlayLayer.getHandle()); + } + void applyMagnificationSpec(MagnificationSpec spec) { applyMagnificationSpec(getPendingTransaction(), spec); getPendingTransaction().apply(); diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 03c0768cdec0..7ae1f24b0cd6 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -29,7 +29,7 @@ import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.server.wm.AppTransition.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED; @@ -454,8 +454,11 @@ public class DockedStackDividerController { inputMethodManagerInternal.hideCurrentInputMethod(); mImeHideRequested = true; } + + // If a primary stack was just created, it will not have access to display content at + // this point so pass it from here to get a valid dock side. final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); - mOriginalDockedSide = stack.getDockSide(); + mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent); return; } mOriginalDockedSide = DOCKED_INVALID; @@ -604,7 +607,7 @@ public class DockedStackDividerController { if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps) && appTransition != TRANSIT_NONE && !AppTransition.isKeyguardGoingAwayTransit(appTransition)) { - mService.showRecentApps(true /* fromHome */); + mService.showRecentApps(); } } diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 0171b56ffc47..d55a64926504 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -18,12 +18,10 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; -import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.content.ClipData; -import android.graphics.PixelFormat; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -32,8 +30,8 @@ import android.os.Message; import android.util.Slog; import android.view.Display; import android.view.IWindow; -import android.view.Surface; import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.View; @@ -50,10 +48,9 @@ class DragDropController { private static final long DRAG_TIMEOUT_MS = 5000; // Messages for Handler. - private static final int MSG_DRAG_START_TIMEOUT = 0; - static final int MSG_DRAG_END_TIMEOUT = 1; - static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 2; - static final int MSG_ANIMATION_END = 3; + static final int MSG_DRAG_END_TIMEOUT = 0; + static final int MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT = 1; + static final int MSG_ANIMATION_END = 2; /** * Drag state per operation. @@ -95,87 +92,35 @@ class DragDropController { mDragState.sendDragStartedIfNeededLocked(window); } - IBinder prepareDrag(SurfaceSession session, int callerPid, - int callerUid, IWindow window, int flags, int width, int height, Surface outSurface) { + IBinder performDrag(SurfaceSession session, int callerPid, int callerUid, IWindow window, + int flags, SurfaceControl surface, int touchSource, float touchX, float touchY, + float thumbCenterX, float thumbCenterY, ClipData data) { if (DEBUG_DRAG) { - Slog.d(TAG_WM, "prepare drag surface: w=" + width + " h=" + height - + " flags=" + Integer.toHexString(flags) + " win=" + window - + " asbinder=" + window.asBinder()); - } - - if (width <= 0 || height <= 0) { - Slog.w(TAG_WM, "width and height of drag shadow must be positive"); - return null; - } - - synchronized (mService.mWindowMap) { - if (dragDropActiveLocked()) { - Slog.w(TAG_WM, "Drag already in progress"); - return null; - } - - // TODO(multi-display): support other displays - final DisplayContent displayContent = - mService.getDefaultDisplayContentLocked(); - final Display display = displayContent.getDisplay(); - - final SurfaceControl surface = new SurfaceControl.Builder(session) - .setName("drag surface") - .setSize(width, height) - .setFormat(PixelFormat.TRANSLUCENT) - .build(); - surface.setLayerStack(display.getLayerStack()); - float alpha = 1; - if ((flags & View.DRAG_FLAG_OPAQUE) == 0) { - alpha = DRAG_SHADOW_ALPHA_TRANSPARENT; - } - surface.setAlpha(alpha); - - if (SHOW_TRANSACTIONS) - Slog.i(TAG_WM, " DRAG " + surface + ": CREATE"); - outSurface.copyFrom(surface); - final IBinder winBinder = window.asBinder(); - IBinder token = new Binder(); - mDragState = new DragState(mService, this, token, surface, flags, winBinder); - mDragState.mPid = callerPid; - mDragState.mUid = callerUid; - mDragState.mOriginalAlpha = alpha; - token = mDragState.mToken = new Binder(); - - // 5 second timeout for this window to actually begin the drag - sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder); - return token; - } - } - - boolean performDrag(IWindow window, IBinder dragToken, - int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, - ClipData data) { - if (DEBUG_DRAG) { - Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data); + Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" + + Integer.toHexString(flags) + " data=" + data); } + final IBinder dragToken = new Binder(); final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data); try { synchronized (mService.mWindowMap) { - mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder()); try { if (!callbackResult) { - return false; + Slog.w(TAG_WM, "IDragDropCallback rejects the performDrag request"); + return null; } - Preconditions.checkState( - mDragState != null, "performDrag() without prepareDrag()"); - Preconditions.checkState( - mDragState.mToken == dragToken, - "performDrag() does not match prepareDrag()"); + if (dragDropActiveLocked()) { + Slog.w(TAG_WM, "Drag already in progress"); + return null; + } final WindowState callingWin = mService.windowForClientLocked( null, window, false); if (callingWin == null) { Slog.w(TAG_WM, "Bad requesting window " + window); - return false; // !!! TODO: throw here? + return null; // !!! TODO: throw here? } // !!! TODO: if input is not still focused on the initiating window, fail @@ -188,18 +133,31 @@ class DragDropController { // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as // the actual drag event dispatch stuff in the dragstate + // !!! TODO(multi-display): support other displays + final DisplayContent displayContent = callingWin.getDisplayContent(); if (displayContent == null) { Slog.w(TAG_WM, "display content is null"); - return false; + return null; } + final float alpha = (flags & View.DRAG_FLAG_OPAQUE) == 0 ? + DRAG_SHADOW_ALPHA_TRANSPARENT : 1; + final IBinder winBinder = window.asBinder(); + IBinder token = new Binder(); + mDragState = new DragState(mService, this, token, surface, flags, winBinder); + surface = null; + mDragState.mPid = callerPid; + mDragState.mUid = callerUid; + mDragState.mOriginalAlpha = alpha; + mDragState.mToken = dragToken; + final Display display = displayContent.getDisplay(); mDragState.register(display); if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel, mDragState.getInputChannel())) { Slog.e(TAG_WM, "Unable to transfer touch focus"); - return false; + return null; } mDragState.mDisplayContent = displayContent; @@ -213,28 +171,31 @@ class DragDropController { // Make the surface visible at the proper location final SurfaceControl surfaceControl = mDragState.mSurfaceControl; if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); - mService.openSurfaceTransaction(); - try { - surfaceControl.setPosition(touchX - thumbCenterX, - touchY - thumbCenterY); - surfaceControl.setLayer(mDragState.getDragLayerLocked()); - surfaceControl.setLayerStack(display.getLayerStack()); - surfaceControl.show(); - } finally { - mService.closeSurfaceTransaction("performDrag"); - if (SHOW_LIGHT_TRANSACTIONS) { - Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); - } + + final SurfaceControl.Transaction transaction = + callingWin.getPendingTransaction(); + transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha); + transaction.setPosition( + surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY); + transaction.show(surfaceControl); + displayContent.reparentToOverlay(transaction, surfaceControl); + callingWin.scheduleAnimation(); + + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); } mDragState.notifyLocationLocked(touchX, touchY); } finally { + if (surface != null) { + surface.release(); + } if (mDragState != null && !mDragState.isInProgress()) { mDragState.closeLocked(); } } } - return true; // success! + return dragToken; // success! } finally { mCallback.get().postPerformDrag(); } @@ -385,21 +346,6 @@ class DragDropController { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_DRAG_START_TIMEOUT: { - IBinder win = (IBinder) msg.obj; - if (DEBUG_DRAG) { - Slog.w(TAG_WM, "Timeout starting drag by win " + win); - } - - synchronized (mService.mWindowMap) { - // !!! TODO: ANR the app that has failed to start the drag in time - if (mDragState != null) { - mDragState.closeLocked(); - } - } - break; - } - case MSG_DRAG_END_TIMEOUT: { final IBinder win = (IBinder) msg.obj; if (DEBUG_DRAG) { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 192d6c84e190..04ae38ec33b1 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -51,6 +51,7 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; @@ -308,30 +309,22 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } /* Drag/drop */ + @Override - public IBinder prepareDrag(IWindow window, int flags, int width, int height, - Surface outSurface) { + public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { final int callerPid = Binder.getCallingPid(); final int callerUid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - return mDragDropController.prepareDrag( - mSurfaceSession, callerPid, callerUid, window, flags, width, height, - outSurface); + return mDragDropController.performDrag(mSurfaceSession, callerPid, callerUid, window, + flags, surface, touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data); } finally { Binder.restoreCallingIdentity(ident); } } @Override - public boolean performDrag(IWindow window, IBinder dragToken, - int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, - ClipData data) { - return mDragDropController.performDrag(window, dragToken, touchSource, - touchX, touchY, thumbCenterX, thumbCenterY, data); - } - - @Override public void reportDropResult(IWindow window, boolean consumed) { final long ident = Binder.clearCallingIdentity(); try { @@ -467,6 +460,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } } + @Override + public void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width, + int height) { + final long identity = Binder.clearCallingIdentity(); + try { + mService.updateTapExcludeRegion(window, regionId, left, top, width, height); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + void windowAddedLocked(String packageName) { mPackageName = packageName; mRelayoutTag = "relayoutWindow: " + mPackageName; diff --git a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java new file mode 100644 index 000000000000..cbc936f2f1d7 --- /dev/null +++ b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java @@ -0,0 +1,56 @@ +/* + * 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 com.android.server.wm; + +import android.graphics.Rect; +import android.graphics.Region; +import android.util.SparseArray; + +/** + * A holder that contains a collection of rectangular areas identified by int id. Each individual + * region can be updated separately. + */ +class TapExcludeRegionHolder { + private SparseArray<Rect> mTapExcludeRects = new SparseArray<>(); + + /** Update the specified region with provided position and size. */ + void updateRegion(int regionId, int left, int top, int width, int height) { + if (width <= 0 || height <= 0) { + // A region became empty - remove it. + mTapExcludeRects.remove(regionId); + return; + } + + Rect region = mTapExcludeRects.get(regionId); + if (region == null) { + region = new Rect(); + } + region.set(left, top, left + width, top + height); + mTapExcludeRects.put(regionId, region); + } + + /** + * Union the provided region with current region formed by this container. + */ + void amendRegion(Region region, Rect boundingRegion) { + for (int i = mTapExcludeRects.size() - 1; i>= 0 ; --i) { + final Rect rect = mTapExcludeRects.valueAt(i); + rect.intersect(boundingRegion); + region.union(rect); + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index fa7ea2ffdd17..26c87b738f8c 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -59,6 +59,8 @@ class TaskPositioner { private static final String TAG_LOCAL = "TaskPositioner"; private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM; + private static Factory sFactory; + // The margin the pointer position has to be within the side of the screen to be // considered at the side of the screen. static final int SIDE_MARGIN_DIP = 100; @@ -214,6 +216,7 @@ class TaskPositioner { } } + /** Use {@link #create(WindowManagerService)} instead **/ TaskPositioner(WindowManagerService service) { mService = service; } @@ -622,4 +625,22 @@ class TaskPositioner { public String toShortString() { return TAG; } + + static void setFactory(Factory factory) { + sFactory = factory; + } + + static TaskPositioner create(WindowManagerService service) { + if (sFactory == null) { + sFactory = new Factory() {}; + } + + return sFactory.create(service); + } + + interface Factory { + default TaskPositioner create(WindowManagerService service) { + return new TaskPositioner(service); + } + } } diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 4dfe290c4f7d..a3f4ee80dfcb 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -126,7 +126,7 @@ class TaskPositioningController { } Display display = displayContent.getDisplay(); - mTaskPositioner = new TaskPositioner(mService); + mTaskPositioner = TaskPositioner.create(mService); mTaskPositioner.register(displayContent); mInputMonitor.updateInputWindowsLw(true /*force*/); diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 8a36226aeaa3..bc0f9ad71a61 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1397,15 +1397,23 @@ public class TaskStack extends WindowContainer<Task> implements return getDockSide(getRawBounds()); } + int getDockSideForDisplay(DisplayContent dc) { + return getDockSide(dc, getRawBounds()); + } + private int getDockSide(Rect bounds) { - if (!inSplitScreenWindowingMode()) { + if (mDisplayContent == null) { return DOCKED_INVALID; } - if (mDisplayContent == null) { + return getDockSide(mDisplayContent, bounds); + } + + private int getDockSide(DisplayContent dc, Rect bounds) { + if (!inSplitScreenWindowingMode()) { return DOCKED_INVALID; } - mDisplayContent.getBounds(mTmpRect); - final int orientation = mDisplayContent.getConfiguration().orientation; + dc.getBounds(mTmpRect); + final int orientation = dc.getConfiguration().orientation; return getDockSideUnchecked(bounds, mTmpRect, orientation); } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index ac0919d93166..1218d3bc1b9b 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -25,7 +25,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a91eb4f42aa2..de1e7ecb2bb9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -88,7 +88,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; @@ -214,15 +213,15 @@ import android.view.PointerIcon; import android.view.RemoteAnimationAdapter; import android.view.Surface; import android.view.SurfaceControl; -import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.TransitionFlags; +import android.view.WindowManager.TransitionType; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; -import android.view.animation.Animation; import android.view.inputmethod.InputMethodManagerInternal; import com.android.internal.R; @@ -1542,7 +1541,7 @@ public class WindowManagerService extends IWindowManager.Stub // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. mOpeningApps.add(atoken); - prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT); + prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT); mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top, frame.width(), frame.height()); executeAppTransition(); @@ -1556,7 +1555,7 @@ public class WindowManagerService extends IWindowManager.Stub // we don't set up the transition anymore and just let it go. if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) { mOpeningApps.add(atoken); - prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT); + prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT); executeAppTransition(); } } @@ -2511,7 +2510,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) { + public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) { prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */); } @@ -2524,8 +2523,8 @@ public class WindowManagerService extends IWindowManager.Stub * AppTransition.TRANSIT_FLAG_*. * @param forceOverride Always override the transit, not matter what was set previously. */ - public void prepareAppTransition(int transit, boolean alwaysKeepCurrent, int flags, - boolean forceOverride) { + public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent, + @TransitionFlags int flags, boolean forceOverride) { if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } @@ -2541,7 +2540,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public int getPendingAppTransition() { + public @TransitionType int getPendingAppTransition() { return mAppTransition.getAppTransition(); } @@ -5968,8 +5967,8 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.lockNow(options); } - public void showRecentApps(boolean fromHome) { - mPolicy.showRecentApps(fromHome); + public void showRecentApps() { + mPolicy.showRecentApps(); } @Override @@ -6614,7 +6613,7 @@ public class WindowManagerService extends IWindowManager.Stub public void onOverlayChanged() { synchronized (mWindowMap) { mPolicy.onOverlayChangedLw(); - mDisplayManagerInternal.onOverlayChanged(); + getDefaultDisplayContentLocked().updateDisplayInfo(); requestTraversal(); } } @@ -6923,6 +6922,23 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * Update a tap exclude region with a rectangular area in the window identified by the provided + * id. Touches on this region will not switch focus to this window. Passing an empty rect will + * remove the area from the exclude region of this window. + */ + void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width, + int height) { + synchronized (mWindowMap) { + final WindowState callingWin = windowForClientLocked(null, client, false); + if (callingWin == null) { + Slog.w(TAG_WM, "Bad requesting window " + client); + return; + } + callingWin.updateTapExcludeRegion(regionId, left, top, width, height); + } + } + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) throws RemoteException { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index db30db074838..477dd2bb9633 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -630,6 +630,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final Point mSurfacePosition = new Point(); /** + * A region inside of this window to be excluded from touch-related focus switches. + */ + private TapExcludeRegionHolder mTapExcludeRegionHolder; + + /** * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms * of z-order and 1 otherwise. */ @@ -1870,6 +1875,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) { dc.mTapExcludedWindows.remove(this); } + if (mTapExcludeRegionHolder != null) { + // If a tap exclude region container was initialized for this window, then it should've + // also been registered in display. + dc.mTapExcludeProvidingWindows.remove(this); + } mPolicy.removeWindowLw(this); disposeInputChannel(); @@ -4620,6 +4630,37 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + /** + * Update a tap exclude region with a rectangular area identified by provided id. The requested + * area will be clipped to the window bounds. + */ + void updateTapExcludeRegion(int regionId, int left, int top, int width, int height) { + final DisplayContent currentDisplay = getDisplayContent(); + if (currentDisplay == null) { + throw new IllegalStateException("Trying to update window not attached to any display."); + } + + if (mTapExcludeRegionHolder == null) { + mTapExcludeRegionHolder = new TapExcludeRegionHolder(); + + // Make sure that this window is registered as one that provides a tap exclude region + // for its containing display. + currentDisplay.mTapExcludeProvidingWindows.add(this); + } + + mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height); + // Trigger touch exclude region update on current display. + final boolean isAppFocusedOnDisplay = mService.mFocusedApp != null + && mService.mFocusedApp.getDisplayContent() == currentDisplay; + currentDisplay.setTouchExcludeRegion(isAppFocusedOnDisplay ? mService.mFocusedApp.getTask() + : null); + } + + /** Union the region with current tap exclude region that this window provides. */ + void amendTapExcludeRegion(Region region) { + mTapExcludeRegionHolder.amendRegion(region, getBounds()); + } + private final class MoveAnimationSpec implements AnimationSpec { private final long mDuration; diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 017b3251696e..7364e87227e4 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -23,23 +23,23 @@ import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; -import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; -import static com.android.server.wm.AppTransition.TRANSIT_NONE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK; -import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; -import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; -import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; +import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; +import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; +import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN; +import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; @@ -57,14 +57,19 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseIntArray; import android.view.Display; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; import android.view.SurfaceControl; +import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.TransitionType; import android.view.animation.Animation; import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Predicate; /** * Positions windows and their surfaces. @@ -247,7 +252,7 @@ class WindowSurfacePlacer { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mService.mAppTransition.getAppTransition(); if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { - transit = AppTransition.TRANSIT_UNSET; + transit = WindowManager.TRANSIT_UNSET; } mService.mSkipAppTransitionAnimation = false; mService.mNoAnimationNotifyOnTransitionFinished.clear(); @@ -255,17 +260,9 @@ class WindowSurfacePlacer { mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); final DisplayContent displayContent = mService.getDefaultDisplayContentLocked(); - // TODO: Don't believe this is really needed... - //mService.mWindowsChanged = true; mService.mRoot.mWallpaperMayChange = false; - // The top-most window will supply the layout params, and we will determine it below. - LayoutParams animLp = null; - int bestAnimLayer = -1; - boolean fullscreenAnim = false; - boolean voiceInteraction = false; - int i; for (i = 0; i < appsCount; i++) { final AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); @@ -274,7 +271,6 @@ class WindowSurfacePlacer { // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the // transition selection depends on wallpaper target visibility. wtoken.clearAnimatingFlags(); - } // Adjust wallpaper before we pull the lower/upper target, since pending changes @@ -283,62 +279,30 @@ class WindowSurfacePlacer { mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent, mService.mOpeningApps); - final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); - boolean openingAppHasWallpaper = false; - boolean closingAppHasWallpaper = false; - - // Do a first pass through the tokens for two things: - // (1) Determine if both the closing and opening app token sets are wallpaper targets, in - // which case special animations are needed (since the wallpaper needs to stay static behind - // them). - // (2) Find the layout params of the top-most application window in the tokens, which is - // what will control the animation theme. - final int closingAppsCount = mService.mClosingApps.size(); - appsCount = closingAppsCount + mService.mOpeningApps.size(); - for (i = 0; i < appsCount; i++) { - final AppWindowToken wtoken; - if (i < closingAppsCount) { - wtoken = mService.mClosingApps.valueAt(i); - if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) { - closingAppHasWallpaper = true; - } - } else { - wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount); - if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) { - openingAppHasWallpaper = true; - } - } - - voiceInteraction |= wtoken.mVoiceInteraction; - - if (wtoken.fillsParent()) { - final WindowState ws = wtoken.findMainWindow(); - if (ws != null) { - animLp = ws.mAttrs; - bestAnimLayer = ws.mLayer; - fullscreenAnim = true; - } - } else if (!fullscreenAnim) { - final WindowState ws = wtoken.findMainWindow(); - if (ws != null) { - if (ws.mLayer > bestAnimLayer) { - animLp = ws.mAttrs; - bestAnimLayer = ws.mLayer; - } - } - } - } + // Determine if closing and opening app token sets are wallpaper targets, in which case + // special animations are needed. + final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null; + final boolean openingAppHasWallpaper = canBeWallpaperTarget(mService.mOpeningApps) + && hasWallpaperTarget; + final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps) + && hasWallpaperTarget; transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, closingAppHasWallpaper); - // If all closing windows are obscured, then there is no need to do an animation. This is - // the case, for example, when this transition is being done behind the lock screen. - if (!mService.mPolicy.allowAppAnimationsLw()) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Animations disallowed by keyguard or dream."); - animLp = null; - } + // Find the layout params of the top-most application window in the tokens, which is + // what will control the animation theme. If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for example, when this transition is being + // done behind a dream window. + final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw() + ? findAnimLayoutParamsToken(transit) + : null; + + final LayoutParams animLp = getAnimLp(animLpToken); + overrideWithRemoteAnimationIfSet(animLpToken, transit); + + final boolean voiceInteraction = containsVoiceInteraction(mService.mOpeningApps) + || containsVoiceInteraction(mService.mOpeningApps); final int layoutRedo; mService.mSurfaceAnimationRunner.deferStartingAnimations(); @@ -388,6 +352,81 @@ class WindowSurfacePlacer { return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; } + private static LayoutParams getAnimLp(AppWindowToken wtoken) { + final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null; + return mainWindow != null ? mainWindow.mAttrs : null; + } + + /** + * Overrides the pending transition with the remote animation defined for the transition in the + * set of defined remote animations in the app window token. + */ + private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit) { + if (animLpToken == null) { + return; + } + final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition(); + if (definition != null) { + final RemoteAnimationAdapter adapter = definition.getAdapter(transit); + if (adapter != null) { + mService.mAppTransition.overridePendingAppTransitionRemote(adapter); + } + } + } + + /** + * @return The window token that determines the animation theme. + */ + private AppWindowToken findAnimLayoutParamsToken(@TransitionType int transit) { + AppWindowToken result; + + // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. + result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, + w -> w.getRemoteAnimationDefinition() != null + && w.getRemoteAnimationDefinition().hasTransition(transit)); + if (result != null) { + return result; + } + result = lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, + w -> w.fillsParent() && w.findMainWindow() != null); + if (result != null) { + return result; + } + return lookForHighestTokenWithFilter(mService.mClosingApps, mService.mOpeningApps, + w -> w.findMainWindow() != null); + } + + private AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1, + ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) { + final int array1count = array1.size(); + final int count = array1count + array2.size(); + int bestPrefixOrderIndex = Integer.MIN_VALUE; + AppWindowToken bestToken = null; + for (int i = 0; i < count; i++) { + final AppWindowToken wtoken; + if (i < array1count) { + wtoken = array1.valueAt(i); + } else { + wtoken = array2.valueAt(i - array1count); + } + final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); + if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) { + bestPrefixOrderIndex = prefixOrderIndex; + bestToken = wtoken; + } + } + return bestToken; + } + + private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) { + for (int i = apps.size() - 1; i >= 0; i--) { + if (apps.valueAt(i).mVoiceInteraction) { + return true; + } + } + return false; + } + private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) { AppWindowToken topOpeningApp = null; diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index b18c1a0ed504..0bc58e024f8a 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -55,6 +55,17 @@ cc_library_static { "frameworks/native/services", "system/gatekeeper/include", ], + + product_variables: { + arc: { + cflags: [ + "-DUSE_ARC", + ], + srcs: [ + "com_android_server_ArcVideoService.cpp", + ], + } + } } cc_defaults { @@ -119,4 +130,17 @@ cc_defaults { "android.hardware.broadcastradio@common-utils-1x-lib", "libscrypt_static", ], + + product_variables: { + arc: { + // TODO: remove the suffix "_bp" after finishing migration to Android.bp. + shared_libs: [ + "libarcbridge", + "libarcbridgeservice", + "libarcvideobridge", + "libchrome", + "libmojo_bp", + ], + } + } } diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp new file mode 100644 index 000000000000..7df8276085ee --- /dev/null +++ b/services/core/jni/com_android_server_ArcVideoService.cpp @@ -0,0 +1,149 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ArcVideoService" + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <media/arcvideobridge/IArcVideoBridge.h> +#include <utils/Log.h> + +#include <base/bind.h> +#include <base/bind_helpers.h> +#include <mojo/edk/embedder/embedder.h> +#include <mojo/public/cpp/bindings/binding.h> + +#include <arc/ArcBridgeSupport.h> +#include <arc/ArcService.h> +#include <arc/Future.h> +#include <arc/IArcBridgeService.h> +#include <arc/MojoProcessSupport.h> +#include <video.mojom.h> + +namespace { + +// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom. +constexpr int kMinimumArcBridgeHostVersion = 6; + +void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version, + mojo::ScopedHandle handle, const std::string& token) { + mojo::edk::ScopedPlatformHandle scoped_platform_handle; + MojoResult result = + mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle); + if (result != MOJO_RESULT_OK) { + ALOGE("Received invalid file descriptor."); + future->set(arc::MojoBootstrapResult()); + return; + } + + base::ScopedFD fd(scoped_platform_handle.release().handle); + future->set(arc::MojoBootstrapResult(std::move(fd), token, version)); +} + +} // namespace + +namespace arc { + +class VideoService : public mojom::VideoInstance, + public ArcService, + public android::BnArcVideoBridge { +public: + explicit VideoService(MojoProcessSupport* mojoProcessSupport) + : mMojoProcessSupport(mojoProcessSupport), mBinding(this) { + mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync( + this, kMinimumArcBridgeHostVersion); + } + + ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); } + + // VideoInstance overrides: + void InitDeprecated(mojom::VideoHostPtr hostPtr) override { + Init(std::move(hostPtr), base::Bind(&base::DoNothing)); + } + + void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override { + ALOGV("Init"); + mHostPtr = std::move(hostPtr); + // A method must be called while we are still in a Mojo thread so the + // proxy can perform lazy initialization and be able to be called from + // non-Mojo threads later. + // This also caches the version number so it can be obtained by calling + // .version(). + mHostPtr.QueryVersion(base::Bind( + [](const InitCallback& callback, uint32_t version) { + ALOGI("VideoService ready (version=%d)", version); + callback.Run(); + }, + callback)); + ALOGV("Init done"); + } + + // ArcService overrides: + void ready(mojom::ArcBridgeHostPtr* bridgeHost) override { + (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind()); + } + + void versionMismatch(uint32_t version) override { + ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version, + kMinimumArcBridgeHostVersion); + } + + // BnArcVideoBridge overrides: + MojoBootstrapResult bootstrapVideoAcceleratorFactory() override { + ALOGV("VideoService::bootstrapVideoAcceleratorFactory"); + + Future<MojoBootstrapResult> future; + mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask( + FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread, + base::Unretained(this), &future)); + return future.get(); + } + + int32_t hostVersion() override { + ALOGV("VideoService::hostVersion"); + return mHostPtr.version(); + } + +private: + void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) { + if (!mHostPtr) { + ALOGE("mHostPtr is not ready yet"); + future->set(MojoBootstrapResult()); + return; + } + mHostPtr->OnBootstrapVideoAcceleratorFactory( + base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version())); + } + + // Outlives VideoService. + MojoProcessSupport* const mMojoProcessSupport; + mojo::Binding<mojom::VideoInstance> mBinding; + mojom::VideoHostPtr mHostPtr; +}; + +} // namespace arc + +namespace android { + +int register_android_server_ArcVideoService() { + defaultServiceManager()->addService( + String16("android.os.IArcVideoBridge"), + new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance())); + return 0; +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 071b6b808ff0..07ddb0561f92 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -52,6 +52,9 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_server_GraphicsStatsService(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); int register_android_server_net_NetworkStatsService(JNIEnv* env); +#ifdef USE_ARC +int register_android_server_ArcVideoService(); +#endif }; using namespace android; @@ -97,6 +100,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_GraphicsStatsService(env); register_android_hardware_display_DisplayViewport(env); register_android_server_net_NetworkStatsService(env); - +#ifdef USE_ARC + register_android_server_ArcVideoService(); +#endif return JNI_VERSION_1_4; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 36de3d12a743..7a0b1bf46fcf 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -39,12 +39,6 @@ import java.util.List; */ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { /** - * To be called by {@link DevicePolicyManagerService#Lifecycle} when the service is started. - * - * @see {@link SystemService#onStart}. - */ - abstract void handleStart(); - /** * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases. * * @see {@link SystemService#onBootPhase}. @@ -109,4 +103,36 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean startUserInBackground(ComponentName who, UserHandle userHandle) { return false; } + + @Override + public void setStartUserSessionMessage( + ComponentName admin, CharSequence startUserSessionMessage) {} + + @Override + public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {} + + @Override + public String getStartUserSessionMessage(ComponentName admin) { + return null; + } + + @Override + public String getEndUserSessionMessage(ComponentName admin) { + return null; + } + + @Override + public void setPrintingEnabled(ComponentName admin, boolean enabled) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPrintingEnabled() { + return true; + } + + @Override + public CharSequence getPrintingDisabledReason() { + return null; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 0d9d85f9a8c5..6bee9d67f215 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -74,6 +74,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.IActivityManager; @@ -197,9 +198,12 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; +import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo; +import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.UserRestrictionsUtils; + import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParser; @@ -284,6 +288,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PASSWORD_VALIDITY = "password-validity"; + private static final String TAG_PRINTING_ENABLED = "printing-enabled"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1); @@ -507,7 +513,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void onStart() { publishBinderService(Context.DEVICE_POLICY_SERVICE, mService); - mService.handleStart(); } @Override @@ -588,6 +593,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { long mPasswordTokenHandle = 0; + boolean mPrintingEnabled = true; + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -647,14 +654,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (Intent.ACTION_USER_ADDED.equals(action)) { - sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle); + sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle); synchronized (DevicePolicyManagerService.this) { // It might take a while for the user to become affiliated. Make security // and network logging unavailable in the meantime. maybePauseDeviceWideLoggingLocked(); } } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle); + sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle); synchronized (DevicePolicyManagerService.this) { // Check whether the user is affiliated, *before* removing its data. boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle); @@ -668,12 +675,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } else if (Intent.ACTION_USER_STARTED.equals(action)) { + sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle); synchronized (DevicePolicyManagerService.this) { maybeSendAdminEnabledBroadcastLocked(userHandle); // Reset the policy data mUserData.remove(userHandle); } handlePackagesChanged(null /* check all admins */, userHandle); + } else if (Intent.ACTION_USER_STOPPED.equals(action)) { + sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle); + } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { + sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { synchronized (DevicePolicyManagerService.this) { maybeSendAdminEnabledBroadcastLocked(userHandle); @@ -682,7 +694,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { handlePackagesChanged(null /* check all admins */, userHandle); } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action) || (Intent.ACTION_PACKAGE_ADDED.equals(action) - && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) { + && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { @@ -692,7 +704,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void sendUserAddedOrRemovedCommand(String action, int userHandle) { + private void sendDeviceOwnerUserCommand(String action, int userHandle) { synchronized (DevicePolicyManagerService.this) { ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner != null) { @@ -792,6 +804,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled"; private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport"; + private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message"; + private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message"; DeviceAdminInfo info; @@ -912,6 +926,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // or null if backups are not mandatory. ComponentName mandatoryBackupTransport = null; + // Message for user switcher + String startUserSessionMessage = null; + String endUserSessionMessage = null; + ActiveAdmin(DeviceAdminInfo _info, boolean parent) { info = _info; isParent = parent; @@ -1180,6 +1198,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.attribute(null, ATTR_VALUE, mandatoryBackupTransport.flattenToString()); out.endTag(null, TAG_MANDATORY_BACKUP_TRANSPORT); } + if (startUserSessionMessage != null) { + out.startTag(null, TAG_START_USER_SESSION_MESSAGE); + out.text(startUserSessionMessage); + out.endTag(null, TAG_START_USER_SESSION_MESSAGE); + } + if (endUserSessionMessage != null) { + out.startTag(null, TAG_END_USER_SESSION_MESSAGE); + out.text(endUserSessionMessage); + out.endTag(null, TAG_END_USER_SESSION_MESSAGE); + } } void writePackageListToXml(XmlSerializer out, String outerTag, @@ -1361,6 +1389,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (TAG_MANDATORY_BACKUP_TRANSPORT.equals(tag)) { mandatoryBackupTransport = ComponentName.unflattenFromString( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + startUserSessionMessage = parser.getText(); + } else { + Log.w(LOG_TAG, "Missing text when loading start session message"); + } + } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + endUserSessionMessage = parser.getText(); + } else { + Log.w(LOG_TAG, "Missing text when loading end session message"); + } } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1702,6 +1744,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(UsageStatsManagerInternal.class); } + NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() { + return LocalServices.getService(NetworkPolicyManagerInternal.class); + } + NotificationManager getNotificationManager() { return mContext.getSystemService(NotificationManager.class); } @@ -1955,6 +2001,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException { return KeyChain.bindAsUser(mContext, user); } + + void postOnSystemServerInitThreadPool(Runnable runnable) { + SystemServerInitThreadPool.get().submit(runnable, LOG_TAG); + } } /** @@ -2010,6 +2060,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); + filter.addAction(Intent.ACTION_USER_STOPPED); + filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); @@ -2849,6 +2901,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_CURRENT_INPUT_METHOD_SET); } + if (!policy.mPrintingEnabled) { + out.startTag(null, TAG_PRINTING_ENABLED); + out.attribute(null, ATTR_VALUE, Boolean.toString(policy.mPrintingEnabled)); + out.endTag(null, TAG_PRINTING_ENABLED); + } + for (final String cert : policy.mOwnerInstalledCaCerts) { out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT); out.attribute(null, ATTR_ALIAS, cert); @@ -3067,6 +3125,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { policy.mCurrentInputMethodSet = true; } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) { policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS)); + } else if (TAG_PRINTING_ENABLED.equals(tag)) { + String enabled = parser.getAttributeValue(null, ATTR_VALUE); + policy.mPrintingEnabled = Boolean.toString(true).equals(enabled); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -3187,6 +3248,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { switch (phase) { case SystemService.PHASE_LOCK_SETTINGS_READY: onLockSettingsReady(); + loadAdminDataAsync(); break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. @@ -3215,10 +3277,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (this) { - // push the force-ephemeral-users policy to the user manager. ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); if (deviceOwner != null) { + // Push the force-ephemeral-users policy to the user manager. mUserManagerInternal.setForceEphemeralUsers(deviceOwner.forceEphemeralUsers); + + // Update user switcher message to activity manager. + ActivityManagerInternal activityManagerInternal = + mInjector.getActivityManagerInternal(); + activityManagerInternal.setSwitchingFromSystemUserMessage( + deviceOwner.startUserSessionMessage); + activityManagerInternal.setSwitchingToSystemUserMessage( + deviceOwner.endUserSessionMessage); } } } @@ -3247,11 +3317,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - void handleStart() { - pushActiveAdminPackages(); - } - - @Override void handleStartUser(int userId) { updateScreenCaptureDisabledInWindowManager(userId, getScreenCaptureDisabled(null, userId)); @@ -3433,6 +3498,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void loadAdminDataAsync() { + mInjector.postOnSystemServerInitThreadPool(() -> { + pushActiveAdminPackages(); + mUsageStatsManagerInternal.onAdminDataAvailable(); + mInjector.getNetworkPolicyManagerInternal().onAdminDataAvailable(); + }); + } + private void pushActiveAdminPackages() { synchronized (this) { final List<UserInfo> users = mUserManager.getUsers(); @@ -10156,7 +10229,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = UserHandle.getUserId(uid); Intent intent = null; if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) || - DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) { + DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) || + DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) { synchronized(this) { final DevicePolicyData policy = getUserData(userId); final int N = policy.mAdminList.size(); @@ -10165,7 +10239,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if ((admin.disableCamera && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) || (admin.disableScreenCapture && DevicePolicyManager - .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) { + .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) || + (admin.mandatoryBackupTransport != null && DevicePolicyManager + .POLICY_MANDATORY_BACKUPS.equals(restriction))) { intent = createShowAdminSupportIntent(admin.info.getComponent(), userId); break; } @@ -12034,15 +12110,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Preconditions.checkNotNull(admin); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); - if (enabled == isLogoutEnabledInternalLocked()) { - // already in the requested state - return; + synchronized (this) { + ActiveAdmin deviceOwner = + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + + if (deviceOwner.isLogoutEnabled == enabled) { + // already in the requested state + return; + } + deviceOwner.isLogoutEnabled = enabled; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } - ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - deviceOwner.isLogoutEnabled = enabled; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } @Override @@ -12051,15 +12130,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } synchronized (this) { - return isLogoutEnabledInternalLocked(); + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + return (deviceOwner != null) && deviceOwner.isLogoutEnabled; } } - private boolean isLogoutEnabledInternalLocked() { - ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); - return (deviceOwner != null) && deviceOwner.isLogoutEnabled; - } - @Override public List<String> getDisallowedSystemApps(ComponentName admin, int userId, String provisioningAction) throws RemoteException { @@ -12096,6 +12171,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId, /* throwForMissingPermission= */ true); checkActiveAdminPrecondition(target, incomingDeviceInfo, policy); + if (!incomingDeviceInfo.getActivityInfo().metaData + .getBoolean(DeviceAdminReceiver.SUPPORT_TRANSFER_OWNERSHIP_META_DATA, false)) { + throw new IllegalArgumentException("Provided target does not support " + + "ownership transfer."); + } final long id = mInjector.binderClearCallingIdentity(); try { @@ -12152,4 +12232,172 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return extras; } + + @Override + public void setStartUserSessionMessage( + ComponentName admin, CharSequence startUserSessionMessage) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(admin); + + final String startUserSessionMessageString = + startUserSessionMessage != null ? startUserSessionMessage.toString() : null; + + synchronized (this) { + final ActiveAdmin deviceOwner = + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + + if (TextUtils.equals(deviceOwner.startUserSessionMessage, startUserSessionMessage)) { + return; + } + deviceOwner.startUserSessionMessage = startUserSessionMessageString; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + + mInjector.getActivityManagerInternal() + .setSwitchingFromSystemUserMessage(startUserSessionMessageString); + } + + @Override + public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(admin); + + final String endUserSessionMessageString = + endUserSessionMessage != null ? endUserSessionMessage.toString() : null; + + synchronized (this) { + final ActiveAdmin deviceOwner = + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + + if (TextUtils.equals(deviceOwner.endUserSessionMessage, endUserSessionMessage)) { + return; + } + deviceOwner.endUserSessionMessage = endUserSessionMessageString; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + + mInjector.getActivityManagerInternal() + .setSwitchingToSystemUserMessage(endUserSessionMessageString); + } + + @Override + public String getStartUserSessionMessage(ComponentName admin) { + if (!mHasFeature) { + return null; + } + Preconditions.checkNotNull(admin); + + synchronized (this) { + final ActiveAdmin deviceOwner = + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + return deviceOwner.startUserSessionMessage; + } + } + + @Override + public String getEndUserSessionMessage(ComponentName admin) { + if (!mHasFeature) { + return null; + } + Preconditions.checkNotNull(admin); + + synchronized (this) { + final ActiveAdmin deviceOwner = + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + return deviceOwner.endUserSessionMessage; + } + } + + private boolean hasPrinting() { + return mInjector.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING); + } + + @Override + public void setPrintingEnabled(ComponentName admin, boolean enabled) { + if (!mHasFeature || !hasPrinting()) { + return; + } + Preconditions.checkNotNull(admin, "Admin cannot be null."); + enforceProfileOrDeviceOwner(admin); + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + if (policy.mPrintingEnabled != enabled) { + policy.mPrintingEnabled = enabled; + saveSettingsLocked(userHandle); + } + } + } + + /** + * Returns whether printing is enabled for current user. + * @hide + */ + @Override + public boolean isPrintingEnabled() { + if (!hasPrinting()) { + return false; + } + if (!mHasFeature) { + return true; + } + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + return policy.mPrintingEnabled; + } + } + + /** + * Returns text of error message if printing is disabled. + * Only to be called by Print Service. + * @hide + */ + @Override + public CharSequence getPrintingDisabledReason() { + if (!hasPrinting() || !mHasFeature) { + Log.e(LOG_TAG, "no printing or no management"); + return null; + } + synchronized (this) { + final int userHandle = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userHandle); + if (policy.mPrintingEnabled) { + Log.e(LOG_TAG, "printing is enabled"); + return null; + } + String ownerPackage = mOwners.getProfileOwnerPackage(userHandle); + if (ownerPackage == null) { + ownerPackage = mOwners.getDeviceOwnerPackageName(); + } + PackageManager pm = mInjector.getPackageManager(); + PackageInfo packageInfo; + try { + packageInfo = pm.getPackageInfo(ownerPackage, 0); + } catch (NameNotFoundException e) { + Log.e(LOG_TAG, "getPackageInfo error", e); + return null; + } + if (packageInfo == null) { + Log.e(LOG_TAG, "packageInfo is inexplicably null"); + return null; + } + ApplicationInfo appInfo = packageInfo.applicationInfo; + if (appInfo == null) { + Log.e(LOG_TAG, "appInfo is inexplicably null"); + return null; + } + CharSequence appLabel = pm.getApplicationLabel(appInfo); + if (appLabel == null) { + Log.e(LOG_TAG, "appLabel is inexplicably null"); + return null; + } + return ((Context) ActivityThread.currentActivityThread().getSystemUiContext()) + .getResources().getString(R.string.printing_disabled_by, appLabel); + } + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8198a186519e..94a356e65878 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -46,10 +46,10 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.os.storage.IStorageManager; -import android.util.TimingsTraceLog; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; +import android.util.TimingsTraceLog; import android.view.WindowManager; import com.android.internal.R; @@ -57,20 +57,21 @@ import com.android.internal.app.ColorDisplayController; import com.android.internal.logging.MetricsLogger; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BinderInternal; -import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.ILockSettings; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.am.ActivityManagerService; import com.android.server.audio.AudioService; +import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.camera.CameraServiceProxy; import com.android.server.car.CarServiceHelperService; import com.android.server.clipboard.ClipboardService; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; -import com.android.server.display.DisplayManagerService; import com.android.server.display.ColorDisplayService; +import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; import com.android.server.fingerprint.FingerprintService; @@ -92,17 +93,16 @@ import com.android.server.om.OverlayManagerService; import com.android.server.os.DeviceIdentifiersPolicyService; import com.android.server.os.SchedulingPolicyService; import com.android.server.pm.BackgroundDexOptService; +import com.android.server.pm.CrossProfileAppsService; import com.android.server.pm.Installer; import com.android.server.pm.LauncherAppsService; import com.android.server.pm.OtaDexoptService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; -import com.android.server.pm.crossprofile.CrossProfileAppsService; import com.android.server.policy.PhoneWindowManager; import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; -import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index cd4e8f977d60..89a5fe1b82c7 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -54,6 +56,7 @@ import android.service.print.PrintServiceDumpProto; import android.util.Log; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.widget.Toast; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; @@ -110,9 +113,12 @@ public final class PrintManagerService extends SystemService { private final SparseArray<UserState> mUserStates = new SparseArray<>(); + private final DevicePolicyManager mDpc; + PrintManagerImpl(Context context) { mContext = context; mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mDpc = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); registerContentObservers(); registerBroadcastReceivers(); } @@ -120,8 +126,26 @@ public final class PrintManagerService extends SystemService { @Override public Bundle print(String printJobName, IPrintDocumentAdapter adapter, PrintAttributes attributes, String packageName, int appId, int userId) { - printJobName = Preconditions.checkStringNotEmpty(printJobName); adapter = Preconditions.checkNotNull(adapter); + if (!isPrintingEnabled()) { + final CharSequence disabledMessage = mDpc.getPrintingDisabledReason(); + if (disabledMessage != null) { + Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage, + Toast.LENGTH_LONG).show(); + } + try { + adapter.start(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.start()"); + } + try { + adapter.finish(); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error calling IPrintDocumentAdapter.finish()"); + } + return null; + } + printJobName = Preconditions.checkStringNotEmpty(printJobName); packageName = Preconditions.checkStringNotEmpty(packageName); final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); @@ -240,7 +264,8 @@ public final class PrintManagerService extends SystemService { @Override public void restartPrintJob(PrintJobId printJobId, int appId, int userId) { - if (printJobId == null) { + if (printJobId == null || !isPrintingEnabled()) { + // if printing is disabled the state just remains "failed". return; } @@ -685,6 +710,10 @@ public final class PrintManagerService extends SystemService { } } + private boolean isPrintingEnabled() { + return mDpc == null || mDpc.isPrintingEnabled(); + } + private void dump(@NonNull DualDumpOutputStream dumpStream, @NonNull ArrayList<UserState> userStatesToDump) { final int userStateCount = userStatesToDump.size(); diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java index 4ff24e9a5e38..e072800e5116 100644 --- a/services/robotests/src/com/android/server/backup/BackupManagerServiceRoboTest.java +++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java @@ -21,9 +21,7 @@ import static com.android.server.backup.testing.TransportData.d2dTransport; import static com.android.server.backup.testing.TransportData.localTransport; import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport; import static com.android.server.backup.testing.TransportTestUtils.setUpTransports; - 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.eq; @@ -39,10 +37,10 @@ import android.app.backup.ISelectBackupTransportCallback; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.os.HandlerThread; import android.platform.test.annotations.Presubmit; import android.provider.Settings; - import com.android.server.backup.testing.ShadowAppBackupUtils; import com.android.server.backup.testing.ShadowBackupPolicyEnforcer; import com.android.server.backup.testing.TransportData; @@ -50,7 +48,10 @@ import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; - +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -62,14 +63,10 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowContextWrapper; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLooper; +import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.shadows.ShadowSettings; import org.robolectric.shadows.ShadowSystemClock; -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, @@ -78,7 +75,7 @@ import java.util.Map; ) @SystemLoaderClasses({RefactoredBackupManagerService.class, TransportManager.class}) @Presubmit -public class BackupManagerServiceRoboTest { +public class BackupManagerServiceTest { private static final String TAG = "BMSTest"; @Mock private TransportManager mTransportManager; @@ -415,6 +412,220 @@ public class BackupManagerServiceRoboTest { mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT); } + /* Tests for updating transport attributes */ + + private static final int PACKAGE_UID = 10; + private ComponentName mTransportComponent; + private int mTransportUid; + + private void setUpForUpdateTransportAttributes() throws Exception { + mTransportComponent = mTransport.getTransportComponent(); + String transportPackage = mTransportComponent.getPackageName(); + + ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager()); + shadowPackageManager.addPackage(transportPackage); + shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage); + + mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0); + } + + @Test + public void + testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mTransportManager) + .updateTransportAttributes( + eq(mTransportComponent), + eq(mTransportName), + eq(configurationIntent), + eq("currentDestinationString"), + eq(dataManagementIntent), + eq("dataManagementLabel")); + } + + @Test + public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid + 1, + mTransportComponent, + mTransportName, + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel")); + } + + @Test + public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + RuntimeException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + null, + mTransportName, + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel")); + } + + @Test + public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + RuntimeException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + null, + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel")); + } + + @Test + public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + RuntimeException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + new Intent(), + null, + new Intent(), + "dataManagementLabel")); + } + + @Test + public void + testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + RuntimeException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + new Intent(), + "currentDestinationString", + null, + "dataManagementLabel")); + + expectThrows( + RuntimeException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + new Intent(), + "currentDestinationString", + new Intent(), + null)); + } + + @Test + public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.grantPermissions(android.Manifest.permission.BACKUP); + Intent configurationIntent = new Intent(); + Intent dataManagementIntent = new Intent(); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + configurationIntent, + "currentDestinationString", + dataManagementIntent, + "dataManagementLabel"); + + verify(mTransportManager) + .updateTransportAttributes( + eq(mTransportComponent), + eq(mTransportName), + eq(configurationIntent), + eq("currentDestinationString"), + eq(dataManagementIntent), + eq("dataManagementLabel")); + } + + @Test + public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException() + throws Exception { + setUpForUpdateTransportAttributes(); + mShadowContext.denyPermissions(android.Manifest.permission.BACKUP); + RefactoredBackupManagerService backupManagerService = + createInitializedBackupManagerService(); + + expectThrows( + SecurityException.class, + () -> + backupManagerService.updateTransportAttributes( + mTransportUid, + mTransportComponent, + mTransportName, + new Intent(), + "currentDestinationString", + new Intent(), + "dataManagementLabel")); + } + /* Miscellaneous tests */ @Test diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java index cf0bc235dc10..068fe8137467 100644 --- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java +++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java @@ -19,9 +19,13 @@ package com.android.server.backup; import static com.android.server.backup.testing.TransportData.genericTransport; import static com.android.server.backup.testing.TransportTestUtils.mockTransport; import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager; - import static com.google.common.truth.Truth.assertThat; - +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.concat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; @@ -31,23 +35,15 @@ import static org.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; -import static java.util.stream.Stream.concat; - import android.annotation.Nullable; +import android.app.backup.BackupManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.platform.test.annotations.Presubmit; - import com.android.server.backup.testing.ShadowContextImplForBackup; -import com.android.server.testing.shadows.FrameworkShadowPackageManager; import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.backup.transport.OnTransportRegisteredListener; @@ -57,7 +53,12 @@ import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; import com.android.server.testing.shadows.FrameworkShadowContextImpl; - +import com.android.server.testing.shadows.FrameworkShadowPackageManager; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -68,12 +69,6 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowPackageManager; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Stream; - @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, @@ -308,6 +303,43 @@ public class TransportManagerTest { } @Test + public void testRegisterAndSelectTransport_whenTransportRegistered() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(null, mTransportA1); + transportManager.registerTransports(); + ComponentName transportComponent = mTransportA1.getTransportComponent(); + + int result = transportManager.registerAndSelectTransport(transportComponent); + + assertThat(result).isEqualTo(BackupManager.SUCCESS); + assertThat(transportManager.getRegisteredTransportComponents()) + .asList() + .contains(transportComponent); + assertThat(transportManager.getCurrentTransportName()) + .isEqualTo(mTransportA1.transportName); + } + + @Test + public void testRegisterAndSelectTransport_whenTransportNotRegistered() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpTransports(mTransportA1); + TransportManager transportManager = createTransportManager(null, mTransportA1); + ComponentName transportComponent = mTransportA1.getTransportComponent(); + + int result = transportManager.registerAndSelectTransport(transportComponent); + + assertThat(result).isEqualTo(BackupManager.SUCCESS); + assertThat(transportManager.getRegisteredTransportComponents()) + .asList() + .contains(transportComponent); + assertThat(transportManager.getTransportDirName(mTransportA1.transportName)) + .isEqualTo(mTransportA1.transportDirName); + assertThat(transportManager.getCurrentTransportName()) + .isEqualTo(mTransportA1.transportName); + } + + @Test public void testGetCurrentTransportName_whenSelectTransportNotCalled_returnsDefaultTransport() throws Exception { setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); @@ -500,6 +532,46 @@ public class TransportManagerTest { () -> transportManager.getTransportDirName(mTransportA2.transportName)); } + @Test + public void testGetRegisteredTransportNames() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + TransportData[] transportsData = {mTransportA1, mTransportA2, mTransportB1}; + setUpTransports(transportsData); + TransportManager transportManager = + createTransportManager(mTransportA1, mTransportA2, mTransportB1); + transportManager.registerTransports(); + + String[] transportNames = transportManager.getRegisteredTransportNames(); + + assertThat(transportNames) + .asList() + .containsExactlyElementsIn( + Stream.of(transportsData) + .map(transportData -> transportData.transportName) + .collect(toList())); + } + + @Test + public void testGetRegisteredTransportComponents() throws Exception { + setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + setUpPackage(PACKAGE_B, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); + TransportData[] transportsData = {mTransportA1, mTransportA2, mTransportB1}; + setUpTransports(transportsData); + TransportManager transportManager = + createTransportManager(mTransportA1, mTransportA2, mTransportB1); + transportManager.registerTransports(); + + ComponentName[] transportNames = transportManager.getRegisteredTransportComponents(); + + assertThat(transportNames) + .asList() + .containsExactlyElementsIn( + Stream.of(transportsData) + .map(TransportData::getTransportComponent) + .collect(toList())); + } + private List<TransportMock> setUpTransports(TransportData... transports) throws Exception { setUpTransportsForTransportManager(mShadowPackageManager, transports); List<TransportMock> transportMocks = new ArrayList<>(transports.length); diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_config_test1.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_config_test1.xml new file mode 100644 index 000000000000..e31fad834023 --- /dev/null +++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_config_test1.xml @@ -0,0 +1,27 @@ +<?xml version='1.0'?> +<watchlist-config> + <sha256-domain> + <!-- test-cc-domain.com --> + <hash>8E7DCD2AEB4F364358242BB3F403263E61E3B4AECE4E2500FF28BF32E52FF0F1</hash> + <!-- test-cc-match-sha256-only.com --> + <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash> + </sha256-domain> + <sha256-ip> + <!-- 127.0.0.2 --> + <hash>1EDD62868F2767A1FFF68DF0A4CB3C23448E45100715768DB9310B5E719536A1</hash> + <!-- 127.0.0.3, match in sha256 only --> + <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2ED</hash> + </sha256-ip> + <crc32-domain> + <!-- test-cc-domain.com --> + <hash>6C67059D</hash> + <!-- test-cc-match-crc32-only.com --> + <hash>3DC775F8</hash> + </crc32-domain> + <crc32-ip> + <!-- 127.0.0.2 --> + <hash>4EBEB612</hash> + <!-- 127.0.0.4, match in crc32 only --> + <hash>A7DD1327</hash> + </crc32-ip> +</watchlist-config> diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml index bb97e9431f72..5349a13280fd 100644 --- a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml +++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml @@ -1,27 +1,4 @@ <?xml version='1.0'?> -<watchlist-settings> - <sha256-domain> - <!-- test-cc-domain.com --> - <hash>8E7DCD2AEB4F364358242BB3F403263E61E3B4AECE4E2500FF28BF32E52FF0F1</hash> - <!-- test-cc-match-sha256-only.com --> - <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash> - </sha256-domain> - <sha256-ip> - <!-- 127.0.0.2 --> - <hash>1EDD62868F2767A1FFF68DF0A4CB3C23448E45100715768DB9310B5E719536A1</hash> - <!-- 127.0.0.3, match in sha256 only --> - <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2ED</hash> - </sha256-ip> - <crc32-domain> - <!-- test-cc-domain.com --> - <hash>6C67059D</hash> - <!-- test-cc-match-crc32-only.com --> - <hash>3DC775F8</hash> - </crc32-domain> - <crc32-ip> - <!-- 127.0.0.2 --> - <hash>4EBEB612</hash> - <!-- 127.0.0.4, match in crc32 only --> - <hash>A7DD1327</hash> - </crc32-ip> -</watchlist-settings> +<network-watchlist-settings> + <secret-key>1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF</secret-key> +</network-watchlist-settings> diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test2.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test2.xml new file mode 100644 index 000000000000..3e65bc06be52 --- /dev/null +++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test2.xml @@ -0,0 +1,4 @@ +<?xml version='1.0'?> +<network-watchlist-settings> + +</network-watchlist-settings> diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java index 6a21931e0418..de54e52d4d0a 100644 --- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java @@ -186,12 +186,13 @@ public class ForceAppStandbyTrackerTest { } private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException { + // Set up functions that start() calls. when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY))) .thenAnswer(inv -> getPowerSaveState()); when(mMockAppOpsManager.getPackagesForOps( any(int[].class) - )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>()); + )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>()); mMockContentResolver = new MockContentResolver(); mFakeSettingsProvider = new FakeSettingsProvider(); @@ -224,6 +225,7 @@ public class ForceAppStandbyTrackerTest { verify(mMockPowerManagerInternal).registerLowPowerModeObserver( eq(ServiceType.FORCE_ALL_APPS_STANDBY), powerSaveObserverCaptor.capture()); + verify(mMockContext).registerReceiver( receiverCaptor.capture(), any(IntentFilter.class)); @@ -511,8 +513,8 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); - // Power save on. - mPowerSaveMode = true; + // Updating to the same state should not fire listener + mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); assertNoCallbacks(l); @@ -550,14 +552,14 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); - verify(l, times(0)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); + verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); reset(l); setAppOps(UID_10_2, PACKAGE_2, false); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt()); - verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); @@ -650,10 +652,20 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); + verify(l, times(1)).updateAllJobs(); + verify(l, times(0)).updateJobsForUid(anyInt()); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); + + verify(l, times(0)).unblockAllUnrestrictedAlarms(); + verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); + reset(l); + instance.setPowerSaveWhitelistAppIds(new int[] {UID_1, UID_2}, new int[] {}); waitUntilMainHandlerDrain(); - verify(l, times(1)).updateAllJobs(); + // Called once for updating all whitelist and once for updating temp whitelist + verify(l, times(2)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); @@ -715,7 +727,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -739,7 +751,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -759,6 +771,15 @@ public class ForceAppStandbyTrackerTest { mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); + verify(l, times(1)).updateAllJobs(); + verify(l, times(0)).updateJobsForUid(eq(UID_10_1)); + verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); + + verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); + reset(l); + mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); @@ -767,7 +788,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -791,7 +812,7 @@ public class ForceAppStandbyTrackerTest { verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); - verify(l, times(0)).unblockAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -839,7 +860,7 @@ public class ForceAppStandbyTrackerTest { } @Test - public void testSmallBatteryAndCharging() throws Exception { + public void testSmallBatteryAndPluggedIn() throws Exception { // This is a small battery device mIsSmallBatteryDevice = true; @@ -853,16 +874,39 @@ public class ForceAppStandbyTrackerTest { Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED)); assertTrue(instance.isForceAllAppsStandbyEnabled()); - // When battery is charging, force app standby is disabled + // When battery is plugged in, force app standby is disabled Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); - intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); mReceiver.onReceive(mMockContext, intent); assertFalse(instance.isForceAllAppsStandbyEnabled()); - // When battery stops charging, force app standby is enabled - intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING); + // When battery stops plugged in, force app standby is enabled + mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + } + + @Test + public void testNotSmallBatteryAndPluggedIn() throws Exception { + // Not a small battery device, so plugged in status should not affect forced app standby + mIsSmallBatteryDevice = false; + + final ForceAppStandbyTrackerTestable instance = newInstance(); + callStart(instance); + assertFalse(instance.isForceAllAppsStandbyEnabled()); + + mPowerSaveMode = true; + mPowerSaveObserver.accept(getPowerSaveState()); + assertTrue(instance.isForceAllAppsStandbyEnabled()); + + // When battery is plugged in, force app standby is unaffected + Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); mReceiver.onReceive(mMockContext, intent); assertTrue(instance.isForceAllAppsStandbyEnabled()); + + // When battery stops plugged in, force app standby is unaffected + mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); + assertTrue(instance.isForceAllAppsStandbyEnabled()); } static int[] array(int... appIds) { diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index fbcccf0fec2a..7c3082fb93de 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -34,6 +35,7 @@ import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEF import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; +import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.Time.TIMEZONE_UTC; @@ -62,6 +64,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -86,13 +89,16 @@ import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkStatsService; import android.net.LinkProperties; +import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicy; import android.net.NetworkState; import android.net.NetworkStats; +import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.net.StringNetworkSpecifier; import android.os.Binder; import android.os.INetworkManagementService; import android.os.PersistableBundle; @@ -105,9 +111,11 @@ import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.Time; +import android.util.DataUnit; import android.util.Log; import android.util.Pair; import android.util.RecurrenceRule; @@ -186,6 +194,7 @@ public class NetworkPolicyManagerServiceTest { private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; private static final String TEST_SSID = "AndroidAP"; + private static final String TEST_IMSI = "310210"; private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID); @@ -309,6 +318,11 @@ public class NetworkPolicyManagerServiceTest { return super.getSystemService(name); } } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + // Assume that we're AID_SYSTEM + } }; setNetpolicyXml(context); @@ -1065,6 +1079,67 @@ public class NetworkPolicyManagerServiceTest { } @Test + public void testRapidNotification() throws Exception { + // Create a place to store fake usage + final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1)); + when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) + .thenAnswer(new Answer<Long>() { + @Override + public Long answer(InvocationOnMock invocation) throws Throwable { + final NetworkStatsHistory.Entry entry = history.getValues( + invocation.getArgument(1), invocation.getArgument(2), null); + return entry.rxBytes + entry.txBytes; + } + }); + + // Define simple data plan which gives us effectively 60MB/day + final SubscriptionPlan plan = SubscriptionPlan.Builder + .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z")) + .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_THROTTLED) + .build(); + mService.setSubscriptionPlans(42, new SubscriptionPlan[] { plan }, + mServiceContext.getOpPackageName()); + + // And get that active network in place + when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] { + new NetworkState(null, new LinkProperties(), + new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR) + .setNetworkSpecifier(new StringNetworkSpecifier("42")), + new Network(42), TEST_IMSI, null) + }); + mService.updateNetworks(); + + // We're 20% through the month (6 days) + final long start = parseTime("2015-11-01T00:00Z"); + final long end = parseTime("2015-11-07T00:00Z"); + setCurrentTimeMillis(end); + + // Using 20% of data in 20% is normal + { + history.removeBucketsBefore(Long.MAX_VALUE); + history.recordData(start, end, + new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0)); + + reset(mNotifManager); + mService.updateNotifications(); + verify(mNotifManager, never()).enqueueNotificationWithTag(any(), any(), any(), + anyInt(), any(), anyInt()); + } + + // Using 80% data in 20% time is alarming + { + history.removeBucketsBefore(Long.MAX_VALUE); + history.recordData(start, end, + new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0)); + + reset(mNotifManager); + mService.updateNotifications(); + verify(mNotifManager, atLeastOnce()).enqueueNotificationWithTag(any(), any(), any(), + anyInt(), any(), anyInt()); + } + } + + @Test public void testMeteredNetworkWithoutLimit() throws Exception { NetworkState[] state = null; NetworkStats stats = null; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java index 1213e814b3fa..e72e4601bbe8 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java @@ -16,13 +16,18 @@ package com.android.server.accessibility; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityService; import android.app.StatusBarManager; import android.content.Context; +import android.os.Handler; +import com.android.internal.util.ScreenshotHelper; import com.android.server.wm.WindowManagerInternal; import org.junit.Before; @@ -39,6 +44,7 @@ public class GlobalActionPerformerTest { @Mock Context mMockContext; @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock StatusBarManager mMockStatusBarManager; + @Mock ScreenshotHelper mMockScreenshotHelper; @Before public void setup() { @@ -48,7 +54,8 @@ public class GlobalActionPerformerTest { .thenReturn(mMockStatusBarManager); mGlobalActionPerformer = - new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal); + new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal, + () -> mMockScreenshotHelper); } @Test @@ -70,4 +77,13 @@ public class GlobalActionPerformerTest { mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG); verify(mMockWindowManagerInternal).showGlobalActions(); } + + @Test + public void testScreenshot_requestsFromScreenshotHelper() { + mGlobalActionPerformer.performGlobalAction( + AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); + verify(mMockScreenshotHelper).takeScreenshot( + eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(), + anyBoolean(), any(Handler.class)); + } } diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java deleted file mode 100644 index 7b4441ae30e6..000000000000 --- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java +++ /dev/null @@ -1,325 +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.backup; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.testng.Assert.expectThrows; - -import android.content.ComponentName; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.os.HandlerThread; -import android.platform.test.annotations.Presubmit; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; -import android.util.Log; - -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; - -import java.io.File; - -@SmallTest -@Presubmit -@RunWith(AndroidJUnit4.class) -// TODO: Migrate this to Robolectric and merge with BackupManagerServiceRoboTest (and remove 'Robo') -public class BackupManagerServiceTest { - private static final String TAG = "BMSTest"; - private static final ComponentName TRANSPORT_COMPONENT = - new ComponentName( - "com.google.android.gms", - "com.google.android.gms.backup.BackupTransportService"); - private static final String TRANSPORT_NAME = TRANSPORT_COMPONENT.flattenToShortString(); - - @Mock private TransportManager mTransportManager; - private Context mContext; - private HandlerThread mBackupThread; - private int mPackageUid; - private File mBaseStateDir; - private File mDataDir; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context baseContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - mContext = spy(new ContextWrapper(baseContext)); - - mBackupThread = new HandlerThread("backup-test"); - mBackupThread.setUncaughtExceptionHandler( - (t, e) -> Log.e(TAG, "Uncaught exception in test thread " + t.getName(), e)); - mBackupThread.start(); - - File cacheDir = mContext.getCacheDir(); - mBaseStateDir = new File(cacheDir, "base_state_dir"); - mDataDir = new File(cacheDir, "data_dir"); - - mPackageUid = - mContext.getPackageManager().getPackageUid(TRANSPORT_COMPONENT.getPackageName(), 0); - } - - @After - public void tearDown() throws Exception { - mBackupThread.quit(); - mBaseStateDir.delete(); - mDataDir.delete(); - } - - @Test - public void testConstructor_callsTransportManagerSetTransportBoundListener() throws Exception { - createBackupManagerService(); - - verify(mTransportManager) - .setOnTransportRegisteredListener(any()); - } - - @Test - public void - testUpdateTransportAttributes_whenTransportUidEqualsToCallingUid_callsThroughToTransportManager() - throws Exception { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - Intent configurationIntent = new Intent(); - Intent dataManagementIntent = new Intent(); - - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - - verify(mTransportManager) - .updateTransportAttributes( - eq(TRANSPORT_COMPONENT), - eq(TRANSPORT_NAME), - eq(configurationIntent), - eq("currentDestinationString"), - eq(dataManagementIntent), - eq("dataManagementLabel")); - } - - @Test - public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException() - throws Exception { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - SecurityException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid + 1, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel")); - } - - @Test - public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - RuntimeException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - null, - TRANSPORT_NAME, - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel")); - } - - @Test - public void testUpdateTransportAttributes_whenNameNull_throwsException() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - RuntimeException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - null, - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel")); - } - - @Test - public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - RuntimeException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - new Intent(), - null, - new Intent(), - "dataManagementLabel")); - } - - @Test - public void - testUpdateTransportAttributes_whenDataManagementArgumentsNullityDontMatch_throwsException() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - RuntimeException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - new Intent(), - "currentDestinationString", - null, - "dataManagementLabel")); - - expectThrows( - RuntimeException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - new Intent(), - "currentDestinationString", - new Intent(), - null)); - } - - @Test - public void - testUpdateTransportAttributes_whenDataManagementArgumentsNull_callsThroughToTransportManager() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - Intent configurationIntent = new Intent(); - - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - configurationIntent, - "currentDestinationString", - null, - null); - - verify(mTransportManager) - .updateTransportAttributes( - eq(TRANSPORT_COMPONENT), - eq(TRANSPORT_NAME), - eq(configurationIntent), - eq("currentDestinationString"), - eq(null), - eq(null)); - } - - @Test - public void - testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager() { - grantBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - Intent configurationIntent = new Intent(); - Intent dataManagementIntent = new Intent(); - - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - configurationIntent, - "currentDestinationString", - dataManagementIntent, - "dataManagementLabel"); - - verify(mTransportManager) - .updateTransportAttributes( - eq(TRANSPORT_COMPONENT), - eq(TRANSPORT_NAME), - eq(configurationIntent), - eq("currentDestinationString"), - eq(dataManagementIntent), - eq("dataManagementLabel")); - } - - @Test - public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException() { - denyBackupPermission(); - RefactoredBackupManagerService backupManagerService = createBackupManagerService(); - - expectThrows( - SecurityException.class, - () -> - backupManagerService.updateTransportAttributes( - mPackageUid, - TRANSPORT_COMPONENT, - TRANSPORT_NAME, - new Intent(), - "currentDestinationString", - new Intent(), - "dataManagementLabel")); - } - - private RefactoredBackupManagerService createBackupManagerService() { - return new RefactoredBackupManagerService( - mContext, - new Trampoline(mContext), - mBackupThread, - mBaseStateDir, - mDataDir, - mTransportManager); - } - - private void grantBackupPermission() { - doNothing().when(mContext).enforceCallingOrSelfPermission(any(), anyString()); - } - - private void denyBackupPermission() { - doThrow(new SecurityException()) - .when(mContext) - .enforceCallingOrSelfPermission( - eq(android.Manifest.permission.BACKUP), anyString()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 5134f523a8ff..06f138ba898b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -45,6 +45,7 @@ import android.view.IWindowManager; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.widget.LockPatternUtils; +import com.android.server.net.NetworkPolicyManagerInternal; import java.io.File; import java.io.IOException; @@ -159,6 +160,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() { + return services.networkPolicyManagerInternal; + } + + @Override PackageManagerInternal getPackageManagerInternal() { return services.packageManagerInternal; } @@ -438,5 +444,10 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) { return services.keyChainConnection; } + + @Override + void postOnSystemServerInitThreadPool(Runnable runnable) { + runnable.run(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 1df0ff23f847..725ede81754a 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -186,6 +186,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { initializeDpms(); Mockito.reset(getServices().usageStatsManagerInternal); + Mockito.reset(getServices().networkPolicyManagerInternal); setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID); setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID); setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID); @@ -211,7 +212,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); - dpms.handleStart(); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED); @@ -283,7 +283,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertNull(LocalServices.getService(DevicePolicyManagerInternal.class)); } - public void testHandleStart() throws Exception { + public void testLoadAdminData() throws Exception { // Device owner in SYSTEM_USER setDeviceOwner(); // Profile owner in CALLER_USER_HANDLE @@ -307,6 +307,23 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkAdminApps(admin2.getPackageName(), adminAnotherPackage.getPackageName()), eq(DpmMockContext.CALLER_USER_HANDLE)); + verify(getServices().usageStatsManagerInternal).onAdminDataAvailable(); + verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable(); + } + + public void testLoadAdminData_noAdmins() throws Exception { + final int ANOTHER_USER_ID = 15; + getServices().addUser(ANOTHER_USER_ID, 0); + + initializeDpms(); + + // Verify + verify(getServices().usageStatsManagerInternal).setActiveAdminApps( + null, DpmMockContext.CALLER_USER_HANDLE); + verify(getServices().usageStatsManagerInternal).setActiveAdminApps( + null, ANOTHER_USER_ID); + verify(getServices().usageStatsManagerInternal).onAdminDataAvailable(); + verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable(); } /** @@ -2119,8 +2136,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertEquals(UserManager.DISALLOW_ADJUST_VOLUME, intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); - // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not - // user restrictions + // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and + // POLICY_MANDATORY_BACKUPS, which are not user restrictions // Camera is not disabled intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA); @@ -2144,6 +2161,19 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE, intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); + // Backups are not mandatory + intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS); + assertNull(intent); + + // Backups are mandatory + ComponentName transportComponent = ComponentName.unflattenFromString( + "android/com.android.internal.backup.LocalTransport"); + dpm.setMandatoryBackupTransport(admin1, transportComponent); + intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS); + assertNotNull(intent); + assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS, + intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION)); + // Same checks for different user mContext.binder.callingUid = DpmMockContext.CALLER_UID; // Camera should be disabled by device owner diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 268d424734e2..0343a52e05da 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -58,6 +58,7 @@ import android.view.IWindowManager; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.widget.LockPatternUtils; +import com.android.server.net.NetworkPolicyManagerInternal; import java.io.File; import java.io.IOException; @@ -76,6 +77,7 @@ public class MockSystemServices { public final UserManager userManager; public final UserManagerInternal userManagerInternal; public final UsageStatsManagerInternal usageStatsManagerInternal; + public final NetworkPolicyManagerInternal networkPolicyManagerInternal; public final PackageManagerInternal packageManagerInternal; public final UserManagerForMock userManagerForMock; public final PowerManagerForMock powerManager; @@ -113,6 +115,8 @@ public class MockSystemServices { userManager = mock(UserManager.class); userManagerInternal = mock(UserManagerInternal.class); usageStatsManagerInternal = mock(UsageStatsManagerInternal.class); + networkPolicyManagerInternal = mock(NetworkPolicyManagerInternal.class); + userManagerForMock = mock(UserManagerForMock.class); packageManagerInternal = mock(PackageManagerInternal.class); powerManager = mock(PowerManagerForMock.class); diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java index 0cc37b48184e..c5f8c90dd6c6 100644 --- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java @@ -58,8 +58,11 @@ public class PersistentDataStoreTest { String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<display-manager-state>\n" + " <brightness-configurations>\n" - + " <brightness-configuration user-serial=\"1\">\n" - + " <brightness-curve>\n" + + " <brightness-configuration" + + " user-serial=\"1\"" + + " package-name=\"example.com\"" + + " timestamp=\"123456\">\n" + + " <brightness-curve description=\"something\">\n" + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n" + " </brightness-curve>\n" @@ -81,6 +84,7 @@ public class PersistentDataStoreTest { float[] expectedNits = { 13.25f, 35.94f }; assertArrayEquals(expectedLux, curve.first, "lux"); assertArrayEquals(expectedNits, curve.second, "nits"); + assertEquals("something", config.getDescription()); config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/); curve = config.getCurve(); @@ -88,6 +92,7 @@ public class PersistentDataStoreTest { expectedNits = new float[] { 13.25f, 15f }; assertArrayEquals(expectedLux, curve.first, "lux"); assertArrayEquals(expectedNits, curve.second, "nits"); + assertNull(config.getDescription()); } @Test @@ -144,12 +149,12 @@ public class PersistentDataStoreTest { public void testStoreAndReloadOfBrightnessConfigurations() { final float[] lux = { 0f, 10f }; final float[] nits = {1f, 100f }; - final BrightnessConfiguration config = new BrightnessConfiguration.Builder() - .setCurve(lux, nits) + final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) + .setDescription("a description") .build(); mDataStore.loadIfNeeded(); assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); - mDataStore.setBrightnessConfigurationForUser(config, 0); + mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename"); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); mInjector.setWriteStream(baos); diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index d8e3be951bd0..43d026d8efc3 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -6,12 +6,17 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.app.job.JobInfo; import android.app.job.JobInfo.Builder; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.net.NetworkRequest; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -24,6 +29,7 @@ import android.util.Pair; import com.android.internal.util.HexDump; import com.android.server.IoThread; +import com.android.server.LocalServices; import com.android.server.job.JobStore.JobSet; import com.android.server.job.controllers.JobStatus; @@ -65,6 +71,13 @@ public class JobStoreTest { JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir()); mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName()); + // Assume all packages are current SDK + final PackageManagerInternal pm = mock(PackageManagerInternal.class); + when(pm.getPackageTargetSdkVersion(anyString())) + .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, pm); + // Freeze the clocks at this moment in time JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java new file mode 100644 index 000000000000..f6a749df1df6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -0,0 +1,169 @@ +/* + * 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 com.android.server.job.controllers; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.job.JobInfo; +import android.content.ComponentName; +import android.content.pm.PackageManagerInternal; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.os.Build; +import android.os.SystemClock; +import android.support.test.runner.AndroidJUnit4; +import android.util.DataUnit; + +import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Clock; +import java.time.ZoneOffset; + +@RunWith(AndroidJUnit4.class) +public class ConnectivityControllerTest { + @Before + public void setUp() throws Exception { + // Assume all packages are current SDK + final PackageManagerInternal pm = mock(PackageManagerInternal.class); + when(pm.getPackageTargetSdkVersion(anyString())) + .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, pm); + + // Freeze the clocks at this moment in time + JobSchedulerService.sSystemClock = + Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); + JobSchedulerService.sUptimeMillisClock = + Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + } + + @Test + public void testInsane() throws Exception { + final Network network = new Network(101); + final JobInfo.Builder job = createJob() + .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + + // Slow network is too slow + assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), network, + createCapabilities().setLinkUpstreamBandwidthKbps(1) + .setLinkDownstreamBandwidthKbps(1))); + // Fast network looks great + assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), network, + createCapabilities().setLinkUpstreamBandwidthKbps(1024) + .setLinkDownstreamBandwidthKbps(1024))); + } + + @Test + public void testCongestion() throws Exception { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final JobInfo.Builder job = createJob() + .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + final JobStatus early = createJobStatus(job, now - 1000, now + 2000); + final JobStatus late = createJobStatus(job, now - 2000, now + 1000); + + // Uncongested network is whenever + { + final Network network = new Network(101); + final NetworkCapabilities capabilities = createCapabilities() + .addCapability(NET_CAPABILITY_NOT_CONGESTED); + assertTrue(ConnectivityController.isSatisfied(early, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); + } + + // Congested network is more selective + { + final Network network = new Network(101); + final NetworkCapabilities capabilities = createCapabilities(); + assertFalse(ConnectivityController.isSatisfied(early, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); + } + } + + @Test + public void testRelaxed() throws Exception { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final JobInfo.Builder job = createJob() + .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1)) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); + final JobStatus early = createJobStatus(job, now - 1000, now + 2000); + final JobStatus late = createJobStatus(job, now - 2000, now + 1000); + + job.setIsPrefetch(true); + final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000); + final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000); + + // Unmetered network is whenever + { + final Network network = new Network(101); + final NetworkCapabilities capabilities = createCapabilities() + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_METERED); + assertTrue(ConnectivityController.isSatisfied(early, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(latePrefetch, network, capabilities)); + } + + // Metered network is only when prefetching and late + { + final Network network = new Network(101); + final NetworkCapabilities capabilities = createCapabilities() + .addCapability(NET_CAPABILITY_NOT_CONGESTED); + assertFalse(ConnectivityController.isSatisfied(early, network, capabilities)); + assertFalse(ConnectivityController.isSatisfied(late, network, capabilities)); + assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(latePrefetch, network, capabilities)); + } + } + + private static NetworkCapabilities createCapabilities() { + return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_VALIDATED); + } + + private static JobInfo.Builder createJob() { + return new JobInfo.Builder(101, new ComponentName("foo", "bar")); + } + + private static JobStatus createJobStatus(JobInfo.Builder job) { + return createJobStatus(job, 0, Long.MAX_VALUE); + } + + private static JobStatus createJobStatus(JobInfo.Builder job, long earliestRunTimeElapsedMillis, + long latestRunTimeElapsedMillis) { + return new JobStatus(job.build(), 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis, + latestRunTimeElapsedMillis, 0, 0, null); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java new file mode 100644 index 000000000000..15c24ac7efd6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -0,0 +1,76 @@ +/* + * 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 com.android.server.job.controllers; + +import static org.junit.Assert.assertEquals; + +import android.app.job.JobInfo; +import android.content.ComponentName; +import android.os.SystemClock; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.job.JobSchedulerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Clock; +import java.time.ZoneOffset; + +@RunWith(AndroidJUnit4.class) +public class JobStatusTest { + private static final double DELTA = 0.00001; + + @Before + public void setUp() throws Exception { + // Freeze the clocks at this moment in time + JobSchedulerService.sSystemClock = + Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); + JobSchedulerService.sUptimeMillisClock = + Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + } + + @Test + public void testFraction() throws Exception { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + assertEquals(1, createJobStatus(0, Long.MAX_VALUE).getFractionRunTime(), DELTA); + + assertEquals(1, createJobStatus(0, now - 1000).getFractionRunTime(), DELTA); + assertEquals(0, createJobStatus(0, now + 1000).getFractionRunTime(), DELTA); + + assertEquals(1, createJobStatus(now - 1000, Long.MAX_VALUE).getFractionRunTime(), DELTA); + assertEquals(0, createJobStatus(now + 1000, Long.MAX_VALUE).getFractionRunTime(), DELTA); + + assertEquals(0, createJobStatus(now, now + 2000).getFractionRunTime(), DELTA); + assertEquals(0.25, createJobStatus(now - 500, now + 1500).getFractionRunTime(), DELTA); + assertEquals(0.5, createJobStatus(now - 1000, now + 1000).getFractionRunTime(), DELTA); + assertEquals(0.75, createJobStatus(now - 1500, now + 500).getFractionRunTime(), DELTA); + assertEquals(1, createJobStatus(now - 2000, now).getFractionRunTime(), DELTA); + } + + private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis, + long latestRunTimeElapsedMillis) { + final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar")) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build(); + return new JobStatus(job, 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis, + latestRunTimeElapsedMillis, 0, 0, null); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index c1789ba0b15a..7eec4fea64dc 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -16,11 +16,11 @@ package com.android.server.locksettings.recoverablekeystore; -import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN; +import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN; -import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD; -import static android.security.keystore.KeychainProtectionParameter.TYPE_PATTERN; -import static android.security.keystore.KeychainProtectionParameter.TYPE_PIN; +import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD; +import static android.security.keystore.KeychainProtectionParams.TYPE_PATTERN; +import static android.security.keystore.KeychainProtectionParams.TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; @@ -77,8 +77,8 @@ public class KeySyncTaskTest { private static final int TEST_USER_ID = 1000; private static final int TEST_RECOVERY_AGENT_UID = 10009; private static final int TEST_RECOVERY_AGENT_UID2 = 10010; - private static final byte[] TEST_DEVICE_ID = - new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}; + private static final byte[] TEST_VAULT_HANDLE = + new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; private static final String TEST_APP_KEY_ALIAS = "rcleaver"; private static final int TEST_GENERATION_ID = 2; private static final int TEST_CREDENTIAL_TYPE = CREDENTIAL_TYPE_PASSWORD; @@ -241,7 +241,7 @@ public class KeySyncTaskTest { public void run_doesNotSendAnythingIfNoRecoveryAgentPendingIntentRegistered() throws Exception { SecretKey applicationKey = generateKey(); mRecoverableKeyStoreDb.setServerParams( - TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_DEVICE_ID); + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID); mRecoverableKeyStoreDb.insertKey( TEST_USER_ID, @@ -278,9 +278,13 @@ public class KeySyncTaskTest { public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception { mRecoverableKeyStoreDb.setRecoveryServicePublicKey( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic()); + + mRecoverableKeyStoreDb.setServerParams( + TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); SecretKey applicationKey = addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); + mKeySyncTask.run(); KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); @@ -293,6 +297,7 @@ public class KeySyncTaskTest { KeyDerivationParams.getSalt(), TEST_CREDENTIAL); Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID); + counterId = 1L; // TODO: use value from the database. assertThat(counterId).isNotNull(); byte[] recoveryKey = decryptThmEncryptedKey( lockScreenHash, @@ -300,10 +305,15 @@ public class KeySyncTaskTest { /*vaultParams=*/ KeySyncUtils.packVaultParams( mKeyPair.getPublic(), counterId, - TEST_DEVICE_ID, - /*maxAttempts=*/ 10)); + /*maxAttempts=*/ 10, + TEST_VAULT_HANDLE)); List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys(); assertThat(applicationKeys).hasSize(1); + assertThat(keychainSnapshot.getCounterId()).isEqualTo(counterId); + assertThat(keychainSnapshot.getMaxAttempts()).isEqualTo(10); + assertThat(keychainSnapshot.getTrustedHardwarePublicKey()) + .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic())); + assertThat(keychainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE); WrappedApplicationKey keyData = applicationKeys.get(0); assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias()); assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias()); @@ -471,7 +481,7 @@ public class KeySyncTaskTest { throws Exception{ SecretKey applicationKey = generateKey(); mRecoverableKeyStoreDb.setServerParams( - userId, recoveryAgentUid, TEST_DEVICE_ID); + userId, recoveryAgentUid, TEST_VAULT_HANDLE); mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, TEST_GENERATION_ID); // Newly added key is not synced. diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java index 3a9ff85520ff..a251c9d7898e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java @@ -50,8 +50,8 @@ public class KeySyncUtilsTest { private static final int RECOVERY_KEY_LENGTH_BITS = 256; private static final int THM_KF_HASH_SIZE = 256; private static final int KEY_CLAIMANT_LENGTH_BYTES = 16; - private static final byte[] TEST_DEVICE_ID = - new byte[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}; + private static final byte[] TEST_VAULT_HANDLE = + new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; private static final String SHA_256_ALGORITHM = "SHA-256"; private static final String APPLICATION_KEY_ALGORITHM = "AES"; private static final byte[] LOCK_SCREEN_HASH_1 = @@ -63,7 +63,8 @@ public class KeySyncUtilsTest { private static final byte[] RECOVERY_RESPONSE_HEADER = "V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8); private static final int PUBLIC_KEY_LENGTH_BYTES = 65; - private static final int VAULT_PARAMS_LENGTH_BYTES = 85; + private static final int VAULT_PARAMS_LENGTH_BYTES = 94; + private static final int VAULT_HANDLE_LENGTH_BYTES = 17; @Test public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception { @@ -344,14 +345,14 @@ public class KeySyncUtilsTest { } @Test - public void packVaultParams_returns85Bytes() throws Exception { + public void packVaultParams_returns94Bytes() throws Exception { PublicKey thmPublicKey = SecureBox.genKeyPair().getPublic(); byte[] packedForm = KeySyncUtils.packVaultParams( thmPublicKey, /*counterId=*/ 1001L, - TEST_DEVICE_ID, - /*maxAttempts=*/ 10); + /*maxAttempts=*/ 10, + TEST_VAULT_HANDLE); assertEquals(VAULT_PARAMS_LENGTH_BYTES, packedForm.length); } @@ -363,8 +364,8 @@ public class KeySyncUtilsTest { byte[] packedForm = KeySyncUtils.packVaultParams( thmPublicKey, /*counterId=*/ 1001L, - TEST_DEVICE_ID, - /*maxAttempts=*/ 10); + /*maxAttempts=*/ 10, + TEST_VAULT_HANDLE); assertArrayEquals( SecureBox.encodePublicKey(thmPublicKey), @@ -378,8 +379,8 @@ public class KeySyncUtilsTest { byte[] packedForm = KeySyncUtils.packVaultParams( SecureBox.genKeyPair().getPublic(), counterId, - TEST_DEVICE_ID, - /*maxAttempts=*/ 10); + /*maxAttempts=*/ 10, + TEST_VAULT_HANDLE); ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm) .order(ByteOrder.LITTLE_ENDIAN); @@ -388,38 +389,37 @@ public class KeySyncUtilsTest { } @Test - public void packVaultParams_encodesDeviceIdAsThirdParam() throws Exception { + public void packVaultParams_encodesMaxAttemptsAsThirdParam() throws Exception { + int maxAttempts = 10; byte[] packedForm = KeySyncUtils.packVaultParams( SecureBox.genKeyPair().getPublic(), - /*counterId=*/ 10021L, - TEST_DEVICE_ID, - /*maxAttempts=*/ 10); + /*counterId=*/ 1001L, + maxAttempts, + TEST_VAULT_HANDLE); ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm) .order(ByteOrder.LITTLE_ENDIAN); byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES); - assertEquals(/* default value*/0, byteBuffer.getLong()); + assertEquals(maxAttempts, byteBuffer.getInt()); } @Test - public void packVaultParams_encodesMaxAttemptsAsLastParam() throws Exception { - int maxAttempts = 10; - + public void packVaultParams_encodesVaultHandleAsLastParam() throws Exception { byte[] packedForm = KeySyncUtils.packVaultParams( SecureBox.genKeyPair().getPublic(), - /*counterId=*/ 1001L, - TEST_DEVICE_ID, - maxAttempts); + /*counterId=*/ 10021L, + /*maxAttempts=*/ 10, + TEST_VAULT_HANDLE); ByteBuffer byteBuffer = ByteBuffer.wrap(packedForm) .order(ByteOrder.LITTLE_ENDIAN); - // TODO: update position. - byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + 2 * Long.BYTES); - assertEquals(maxAttempts, byteBuffer.getInt()); + byteBuffer.position(PUBLIC_KEY_LENGTH_BYTES + Long.BYTES + Integer.BYTES); + byte[] vaultHandle = new byte[VAULT_HANDLE_LENGTH_BYTES]; + byteBuffer.get(vaultHandle); + assertArrayEquals(TEST_VAULT_HANDLE, vaultHandle); } - private static byte[] randomBytes(int n) { byte[] bytes = new byte[n]; new Random().nextBytes(bytes); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 37157428683a..970bc33337da 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -16,8 +16,8 @@ package com.android.server.locksettings.recoverablekeystore; -import static android.security.keystore.KeychainProtectionParameter.TYPE_LOCKSCREEN; -import static android.security.keystore.KeychainProtectionParameter.TYPE_PASSWORD; +import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN; +import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; @@ -43,7 +43,7 @@ import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.KeyDerivationParams; -import android.security.keystore.KeychainProtectionParameter; +import android.security.keystore.KeychainProtectionParams; import android.security.keystore.WrappedApplicationKey; import android.support.test.filters.SmallTest; import android.support.test.InstrumentationRegistry; @@ -250,7 +250,7 @@ public class RecoverableKeyStoreManagerTest { TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, ImmutableList.of( - new KeychainProtectionParameter( + new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -269,7 +269,7 @@ public class RecoverableKeyStoreManagerTest { TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, ImmutableList.of( - new KeychainProtectionParameter( + new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -283,6 +283,44 @@ public class RecoverableKeyStoreManagerTest { } @Test + public void closeSession_closesASession() throws Exception { + mRecoverableKeyStoreManager.startRecoverySession( + TEST_SESSION_ID, + TEST_PUBLIC_KEY, + TEST_VAULT_PARAMS, + TEST_VAULT_CHALLENGE, + ImmutableList.of( + new KeychainProtectionParams( + TYPE_LOCKSCREEN, + TYPE_PASSWORD, + KeyDerivationParams.createSha256Params(TEST_SALT), + TEST_SECRET))); + + mRecoverableKeyStoreManager.closeSession(TEST_SESSION_ID); + + assertEquals(0, mRecoverySessionStorage.size()); + } + + @Test + public void closeSession_doesNotCloseUnrelatedSessions() throws Exception { + mRecoverableKeyStoreManager.startRecoverySession( + TEST_SESSION_ID, + TEST_PUBLIC_KEY, + TEST_VAULT_PARAMS, + TEST_VAULT_CHALLENGE, + ImmutableList.of( + new KeychainProtectionParams( + TYPE_LOCKSCREEN, + TYPE_PASSWORD, + KeyDerivationParams.createSha256Params(TEST_SALT), + TEST_SECRET))); + + mRecoverableKeyStoreManager.closeSession("some random session"); + + assertEquals(1, mRecoverySessionStorage.size()); + } + + @Test public void startRecoverySession_throwsIfBadNumberOfSecrets() throws Exception { try { mRecoverableKeyStoreManager.startRecoverySession( @@ -292,9 +330,9 @@ public class RecoverableKeyStoreManagerTest { TEST_VAULT_CHALLENGE, ImmutableList.of()); fail("should have thrown"); - } catch (ServiceSpecificException e) { + } catch (UnsupportedOperationException e) { assertThat(e.getMessage()).startsWith( - "Only a single KeychainProtectionParameter is supported"); + "Only a single KeychainProtectionParams is supported"); } } @@ -307,7 +345,7 @@ public class RecoverableKeyStoreManagerTest { TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, ImmutableList.of( - new KeychainProtectionParameter( + new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -329,7 +367,7 @@ public class RecoverableKeyStoreManagerTest { vaultParams, TEST_VAULT_CHALLENGE, ImmutableList.of( - new KeychainProtectionParameter( + new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -362,7 +400,7 @@ public class RecoverableKeyStoreManagerTest { TEST_PUBLIC_KEY, TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, - ImmutableList.of(new KeychainProtectionParameter( + ImmutableList.of(new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -386,7 +424,7 @@ public class RecoverableKeyStoreManagerTest { TEST_PUBLIC_KEY, TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, - ImmutableList.of(new KeychainProtectionParameter( + ImmutableList.of(new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), @@ -418,7 +456,7 @@ public class RecoverableKeyStoreManagerTest { TEST_PUBLIC_KEY, TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, - ImmutableList.of(new KeychainProtectionParameter( + ImmutableList.of(new KeychainProtectionParams( TYPE_LOCKSCREEN, TYPE_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java index 5cb7b677dbbd..f0254c6d5dfe 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java @@ -28,8 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import android.content.Context; -import android.content.SharedPreferences; -import android.security.keystore.RecoveryManager; +import android.security.keystore.RecoveryController; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -283,7 +282,7 @@ public class RecoverableKeyStoreDbTest { Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid); assertThat(statuses).hasSize(3); - assertThat(statuses).containsEntry(alias, RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS); + assertThat(statuses).containsEntry(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS); assertThat(statuses).containsEntry(alias2, status); assertThat(statuses).containsEntry(alias3, status); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java index 6f93fe409e48..bb0474efee58 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorageTest.java @@ -19,6 +19,8 @@ package com.android.server.locksettings.recoverablekeystore.storage; import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -88,6 +90,45 @@ public class RecoverySessionStorageTest { } @Test + public void remove_deletesSpecificSession() { + RecoverySessionStorage storage = new RecoverySessionStorage(); + storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry( + TEST_SESSION_ID, + lskfHashFixture(), + keyClaimantFixture(), + vaultParamsFixture())); + storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry( + "some other session", + lskfHashFixture(), + keyClaimantFixture(), + vaultParamsFixture())); + + storage.remove(TEST_USER_ID, TEST_SESSION_ID); + + assertNull(storage.get(TEST_USER_ID, TEST_SESSION_ID)); + } + + @Test + public void remove_doesNotDeleteOtherSessions() { + String otherSessionId = "some other session"; + RecoverySessionStorage storage = new RecoverySessionStorage(); + storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry( + TEST_SESSION_ID, + lskfHashFixture(), + keyClaimantFixture(), + vaultParamsFixture())); + storage.add(TEST_USER_ID, new RecoverySessionStorage.Entry( + otherSessionId, + lskfHashFixture(), + keyClaimantFixture(), + vaultParamsFixture())); + + storage.remove(TEST_USER_ID, TEST_SESSION_ID); + + assertNotNull(storage.get(TEST_USER_ID, otherSessionId)); + } + + @Test public void destroy_overwritesLskfHashMemory() { RecoverySessionStorage storage = new RecoverySessionStorage(); RecoverySessionStorage.Entry entry = new RecoverySessionStorage.Entry( diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java new file mode 100644 index 000000000000..a31b46ce5534 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 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.net.watchlist; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.privacy.DifferentialPrivacyEncoder; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * runtest frameworks-services -c com.android.server.net.watchlist.PrivacyUtilsTests + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class PrivacyUtilsTests { + + private static final List<String> TEST_DIGEST_LIST = Arrays.asList( + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", + "E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48", + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"); + + private static final WatchlistReportDbHelper.AggregatedResult TEST_AGGREGATED_RESULT1 = + new WatchlistReportDbHelper.AggregatedResult(new HashSet<>(Arrays.asList( + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48", + "E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45")), null, + new HashMap<>()); + + private static final byte[] TEST_SECRET = new byte[]{ + (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93, + (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54, + (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93, + (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54, + (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93, + (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54, + (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54 + }; + + @Test + public void testPrivacyUtils_encodeReport() throws Exception { + Map<String, Boolean> result = PrivacyUtils.createDpEncodedReportMap(false, null, + TEST_DIGEST_LIST, TEST_AGGREGATED_RESULT1); + assertEquals(6, result.size()); + assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48")); + assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49")); + assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47")); + assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45")); + assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44")); + assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43")); + } + + @Test + public void testPrivacyUtils_createInsecureDPEncoderForTest() throws Exception { + DifferentialPrivacyEncoder encoder = PrivacyUtils.createInsecureDPEncoderForTest("foo"); + assertEquals( + "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, " + + "ProbabilityQ: 1.000", + encoder.getConfig().toString()); + assertTrue(encoder.isInsecureEncoderForTest()); + } + + @Test + public void testPrivacyUtils_createSecureDPEncoderTest() throws Exception { + DifferentialPrivacyEncoder encoder = PrivacyUtils.createSecureDPEncoder(TEST_SECRET, "foo"); + assertEquals( + "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, " + + "ProbabilityQ: 1.000", + encoder.getConfig().toString()); + assertFalse(encoder.isInsecureEncoderForTest()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java new file mode 100644 index 000000000000..395969eb7c77 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/ReportUtilsTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 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.net.watchlist; + +import static org.junit.Assert.assertArrayEquals; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.internal.util.HexDump; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; + +/** + * runtest frameworks-services -c com.android.server.net.watchlist.ReportUtilsTests + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ReportUtilsTests { + + private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_config_test1.xml"; + private static final String TEST_XML_1_HASH = + "C99F27A08B1FDB15B101098E12BB2A0AA0D474E23C50F24920A52AB2322BFD94"; + private static final String REPORT_HEADER_MAGIC = "8D370AAC"; + private static final String REPORT_HEADER_VERSION = "0001"; + + private Context mContext; + private File mTestXmlFile; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getContext(); + mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_config.xml"); + mTestXmlFile.delete(); + } + + @After + public void tearDown() throws Exception { + mTestXmlFile.delete(); + } + + @Test + public void testReportUtils_serializeReport() throws Exception { + final byte[] expectedResult = HexDump.hexStringToByteArray( + REPORT_HEADER_MAGIC + REPORT_HEADER_VERSION + TEST_XML_1_HASH + + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43" + "01" + + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44" + "00" + + "D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45" + "00" + + "E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46" + "01" + + "F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47" + "01" + ); + HashMap<String, Boolean> input = new HashMap<>(); + input.put("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", false); + input.put("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", true); + input.put("D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", false); + input.put("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46", true); + input.put("F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", true); + + copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile); + WatchlistConfig config = new WatchlistConfig(mTestXmlFile); + + byte[] result = ReportEncoder.serializeReport(config, input); + assertArrayEquals(expectedResult, result); + } + + private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile) + throws IOException { + writeToFile(outFile, readAsset(context, xmlAsset)); + } + + private static String readAsset(Context context, String assetPath) throws IOException { + final StringBuilder sb = new StringBuilder(); + try (BufferedReader br = new BufferedReader( + new InputStreamReader( + context.getResources().getAssets().open(assetPath)))) { + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + sb.append(System.lineSeparator()); + } + } + return sb.toString(); + } + + private static void writeToFile(File path, String content) + throws IOException { + path.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(path)) { + writer.write(content); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java new file mode 100644 index 000000000000..654acc20328d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 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.net.watchlist; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.internal.util.HexDump; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.util.Arrays; + + +/** + * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WatchlistConfigTests { + + private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_config_test1.xml"; + private static final String TEST_XML_1_HASH = + "C99F27A08B1FDB15B101098E12BB2A0AA0D474E23C50F24920A52AB2322BFD94"; + private static final String TEST_CC_DOMAIN = "test-cc-domain.com"; + private static final String TEST_CC_IP = "127.0.0.2"; + private static final String TEST_NOT_EXIST_CC_DOMAIN = "test-not-exist-cc-domain.com"; + private static final String TEST_NOT_EXIST_CC_IP = "1.2.3.4"; + private static final String TEST_SHA256_ONLY_DOMAIN = "test-cc-match-sha256-only.com"; + private static final String TEST_SHA256_ONLY_IP = "127.0.0.3"; + private static final String TEST_CRC32_ONLY_DOMAIN = "test-cc-match-crc32-only.com"; + private static final String TEST_CRC32_ONLY_IP = "127.0.0.4"; + + private static final String TEST_NEW_CC_DOMAIN = "test-new-cc-domain.com"; + private static final byte[] TEST_NEW_CC_DOMAIN_SHA256 = HexDump.hexStringToByteArray( + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"); + private static final byte[] TEST_NEW_CC_DOMAIN_CRC32 = HexDump.hexStringToByteArray("76795BD3"); + + private static final String TEST_NEW_CC_IP = "1.1.1.2"; + private static final byte[] TEST_NEW_CC_IP_SHA256 = HexDump.hexStringToByteArray( + "721BAB5E313CF0CC76B10F9592F18B9D1B8996497501A3306A55B3AE9F1CC87C"); + private static final byte[] TEST_NEW_CC_IP_CRC32 = HexDump.hexStringToByteArray("940B8BEE"); + + private Context mContext; + private File mTestXmlFile; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getContext(); + mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_config.xml"); + mTestXmlFile.delete(); + } + + @After + public void tearDown() throws Exception { + mTestXmlFile.delete(); + } + + @Test + public void testWatchlistConfig_parsing() throws Exception { + copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile); + WatchlistConfig config = new WatchlistConfig(mTestXmlFile); + assertTrue(config.containsDomain(TEST_CC_DOMAIN)); + assertTrue(config.containsIp(TEST_CC_IP)); + assertFalse(config.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); + assertFalse(config.containsIp(TEST_NOT_EXIST_CC_IP)); + assertFalse(config.containsDomain(TEST_SHA256_ONLY_DOMAIN)); + assertFalse(config.containsIp(TEST_SHA256_ONLY_IP)); + assertFalse(config.containsDomain(TEST_CRC32_ONLY_DOMAIN)); + assertFalse(config.containsIp(TEST_CRC32_ONLY_IP)); + } + + @Test + public void testWatchlistConfig_noXml() throws Exception { + WatchlistConfig config = new WatchlistConfig(mTestXmlFile); + assertFalse(config.containsDomain(TEST_CC_DOMAIN)); + assertFalse(config.containsIp(TEST_CC_IP)); + assertFalse(config.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); + assertFalse(config.containsIp(TEST_NOT_EXIST_CC_IP)); + assertFalse(config.containsDomain(TEST_SHA256_ONLY_DOMAIN)); + assertFalse(config.containsIp(TEST_SHA256_ONLY_IP)); + assertFalse(config.containsDomain(TEST_CRC32_ONLY_DOMAIN)); + assertFalse(config.containsIp(TEST_CRC32_ONLY_IP)); + } + + @Test + public void testWatchlistConfig_getWatchlistConfigHash() throws Exception { + copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile); + WatchlistConfig config = new WatchlistConfig(mTestXmlFile); + assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash())); + } + + @Test + public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception { + WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml")); + ByteArrayOutputStream bs = new ByteArrayOutputStream(2048); + PrintWriter pw = new PrintWriter(bs); + // Make sure dump still works even watchlist does not exist + config.dump(null, pw, null); + } + + private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile) + throws IOException { + writeToFile(outFile, readAsset(context, xmlAsset)); + } + + private static String readAsset(Context context, String assetPath) throws IOException { + final StringBuilder sb = new StringBuilder(); + try (BufferedReader br = new BufferedReader( + new InputStreamReader( + context.getResources().getAssets().open(assetPath)))) { + String line; + while ((line = br.readLine()) != null) { + sb.append(line); + sb.append(System.lineSeparator()); + } + } + return sb.toString(); + } + + private static void writeToFile(File path, String content) + throws IOException { + path.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(path)) { + writer.write(content); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java index e356b13d01d5..070de5b95356 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java @@ -36,14 +36,6 @@ import java.util.Arrays; @SmallTest public class WatchlistLoggingHandlerTests { - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - @Test public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception { String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm"); diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java index 212d25d42420..07158afbaaff 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright 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. @@ -16,8 +16,8 @@ package com.android.server.net.watchlist; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import android.content.Context; import android.support.test.InstrumentationRegistry; @@ -36,7 +36,6 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Arrays; /** * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistSettingsTests @@ -46,24 +45,9 @@ import java.util.Arrays; public class WatchlistSettingsTests { private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_settings_test1.xml"; - private static final String TEST_CC_DOMAIN = "test-cc-domain.com"; - private static final String TEST_CC_IP = "127.0.0.2"; - private static final String TEST_NOT_EXIST_CC_DOMAIN = "test-not-exist-cc-domain.com"; - private static final String TEST_NOT_EXIST_CC_IP = "1.2.3.4"; - private static final String TEST_SHA256_ONLY_DOMAIN = "test-cc-match-sha256-only.com"; - private static final String TEST_SHA256_ONLY_IP = "127.0.0.3"; - private static final String TEST_CRC32_ONLY_DOMAIN = "test-cc-match-crc32-only.com"; - private static final String TEST_CRC32_ONLY_IP = "127.0.0.4"; - - private static final String TEST_NEW_CC_DOMAIN = "test-new-cc-domain.com"; - private static final byte[] TEST_NEW_CC_DOMAIN_SHA256 = HexDump.hexStringToByteArray( - "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"); - private static final byte[] TEST_NEW_CC_DOMAIN_CRC32 = HexDump.hexStringToByteArray("76795BD3"); - - private static final String TEST_NEW_CC_IP = "1.1.1.2"; - private static final byte[] TEST_NEW_CC_IP_SHA256 = HexDump.hexStringToByteArray( - "721BAB5E313CF0CC76B10F9592F18B9D1B8996497501A3306A55B3AE9F1CC87C"); - private static final byte[] TEST_NEW_CC_IP_CRC32 = HexDump.hexStringToByteArray("940B8BEE"); + private static final String TEST_XML_2 = "NetworkWatchlistTest/watchlist_settings_test2.xml"; + private static final String HARD_CODED_SECRET_KEY = "1234567890ABCDEF1234567890ABCDEF" + + "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF"; private Context mContext; private File mTestXmlFile; @@ -71,7 +55,7 @@ public class WatchlistSettingsTests { @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getContext(); - mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_settings.xml"); + mTestXmlFile = new File(mContext.getFilesDir(), "test_settings_config.xml"); mTestXmlFile.delete(); } @@ -84,55 +68,48 @@ public class WatchlistSettingsTests { public void testWatchlistSettings_parsing() throws Exception { copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile); WatchlistSettings settings = new WatchlistSettings(mTestXmlFile); - assertTrue(settings.containsDomain(TEST_CC_DOMAIN)); - assertTrue(settings.containsIp(TEST_CC_IP)); - assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP)); - assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP)); - assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP)); + assertEquals(HARD_CODED_SECRET_KEY, HexDump.toHexString(settings.getPrivacySecretKey())); + // Try again. + assertEquals(HARD_CODED_SECRET_KEY, HexDump.toHexString(settings.getPrivacySecretKey())); } @Test - public void testWatchlistSettings_writeSettingsToMemory() throws Exception { - copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile); + public void testWatchlistSettings_parsingWithoutKey() throws Exception { + copyWatchlistSettingsXml(mContext, TEST_XML_2, mTestXmlFile); WatchlistSettings settings = new WatchlistSettings(mTestXmlFile); - settings.writeSettingsToMemory(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32), - Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32), - Arrays.asList(TEST_NEW_CC_IP_SHA256)); - // Ensure old watchlist is not in memory - assertFalse(settings.containsDomain(TEST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_CC_IP)); - assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP)); - assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP)); - assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP)); - // Ensure new watchlist is in memory - assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN)); - assertTrue(settings.containsIp(TEST_NEW_CC_IP)); - // Reload settings from disk and test again + final String tmpKey1 = HexDump.toHexString(settings.getPrivacySecretKey()); + assertNotEquals(HARD_CODED_SECRET_KEY, tmpKey1); + assertEquals(96, tmpKey1.length()); + // Try again to make sure it's the same. + assertEquals(tmpKey1, HexDump.toHexString(settings.getPrivacySecretKey())); + // Create new settings object again to make sure it can get the new saved key. settings = new WatchlistSettings(mTestXmlFile); - // Ensure old watchlist is in memory - assertTrue(settings.containsDomain(TEST_CC_DOMAIN)); - assertTrue(settings.containsIp(TEST_CC_IP)); - assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP)); - assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP)); - assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN)); - assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP)); - // Ensure new watchlist is not in memory - assertFalse(settings.containsDomain(TEST_NEW_CC_DOMAIN)); - assertFalse(settings.containsIp(TEST_NEW_CC_IP));; + assertEquals(tmpKey1, HexDump.toHexString(settings.getPrivacySecretKey())); + } + + @Test + public void testWatchlistSettings_noExistingXml() throws Exception { + WatchlistSettings settings = new WatchlistSettings(mTestXmlFile); + final String tmpKey1 = HexDump.toHexString(settings.getPrivacySecretKey()); + assertNotEquals(HARD_CODED_SECRET_KEY, tmpKey1); + assertEquals(96, tmpKey1.length()); + // Try again to make sure it's the same. + assertEquals(tmpKey1, HexDump.toHexString(settings.getPrivacySecretKey())); + // Create new settings object again to make sure it can get the new saved key. + settings = new WatchlistSettings(mTestXmlFile); + assertEquals(tmpKey1, HexDump.toHexString(settings.getPrivacySecretKey())); + // Delete xml and generate key again, to make sure key is randomly generated. + mTestXmlFile.delete(); + settings = new WatchlistSettings(mTestXmlFile); + final String tmpKey2 = HexDump.toHexString(settings.getPrivacySecretKey()); + assertNotEquals(HARD_CODED_SECRET_KEY, tmpKey2); + assertNotEquals(tmpKey1, tmpKey2); + assertEquals(96, tmpKey2.length()); } private static void copyWatchlistSettingsXml(Context context, String xmlAsset, File outFile) throws IOException { writeToFile(outFile, readAsset(context, xmlAsset)); - } private static String readAsset(Context context, String assetPath) throws IOException { diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java index ff55a2ba120b..c69437dc798e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java @@ -1,9 +1,7 @@ -package com.android.server.pm.crossprofile; +package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.assertEquals; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -34,7 +32,6 @@ import android.util.SparseArray; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -44,7 +41,7 @@ import java.util.List; /** * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.pm.crossprofile.CrossProfileAppsServiceImplTest + * atest FrameworksServicesTests:com.android.server.pm.CrossProfileAppsServiceImplTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java index 77f96ca37e36..e7e55cd4404e 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java @@ -16,9 +16,9 @@ package com.android.server.wm; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY; -import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static org.junit.Assert.assertEquals; import android.content.Context; diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java index ac291632c877..57da6a3a60a6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java @@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; @@ -28,6 +27,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.ClipData; +import android.graphics.PixelFormat; import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; @@ -36,7 +36,7 @@ import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.InputChannel; -import android.view.Surface; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import com.android.internal.annotations.GuardedBy; @@ -146,14 +146,6 @@ public class DragDropControllerTests extends WindowTestsBase { } @Test - public void testPrepareDrag_ZeroSizeSurface() throws Exception { - final Surface surface = new Surface(); - mToken = mTarget.prepareDrag( - new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface); - assertNull(mToken); - } - - @Test public void testPerformDrag_NullDataWithGrantUri() throws Exception { dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0); } @@ -169,16 +161,24 @@ public class DragDropControllerTests extends WindowTestsBase { } private void dragFlow(int flag, ClipData data, float dropX, float dropY) { - final Surface surface = new Surface(); - mToken = mTarget.prepareDrag( - new SurfaceSession(), 0, 0, mWindow.mClient, flag, 100, 100, surface); - assertNotNull(mToken); - - assertTrue(sWm.mInputManager.transferTouchFocus(null, null)); - assertTrue(mTarget.performDrag( - mWindow.mClient, mToken, 0, 0, 0, 0, 0, data)); - - mTarget.handleMotionEvent(false, dropX, dropY); - mToken = mWindow.mClient.asBinder(); + final SurfaceSession appSession = new SurfaceSession(); + try { + final SurfaceControl surface = new SurfaceControl.Builder(appSession) + .setName("drag surface") + .setSize(100, 100) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); + + assertTrue(sWm.mInputManager.transferTouchFocus(null, null)); + mToken = mTarget.performDrag( + new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, + data); + assertNotNull(mToken); + + mTarget.handleMotionEvent(false, dropX, dropY); + mToken = mWindow.mClient.asBinder(); + } finally { + appSession.kill(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java index 873a01bd8116..7bf7dd78711c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java @@ -33,6 +33,7 @@ import static com.android.server.wm.WindowManagerService.dipToPixel; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** @@ -57,6 +58,9 @@ public class TaskPositionerTests extends WindowTestsBase { @Before public void setUp() throws Exception { super.setUp(); + + TaskPositioner.setFactory(null); + final Display display = mDisplayContent.getDisplay(); final DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); @@ -65,10 +69,26 @@ public class TaskPositionerTests extends WindowTestsBase { mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm); mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm); - mPositioner = new TaskPositioner(sWm); + mPositioner = TaskPositioner.create(sWm); mPositioner.register(mDisplayContent); } + @Test + public void testOverrideFactory() throws Exception { + final boolean[] created = new boolean[1]; + created[0] = false; + TaskPositioner.setFactory(new TaskPositioner.Factory() { + @Override + public TaskPositioner create(WindowManagerService service) { + created[0] = true; + return null; + } + }); + + assertNull(TaskPositioner.create(sWm)); + assertTrue(created[0]); + } + /** * This tests that free resizing will allow to change the orientation as well * as does some basic tests (e.g. dragging in Y only will keep X stable). diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java index f253632a1765..920796ed6a30 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -18,7 +18,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; -import static com.android.server.wm.AppTransition.TRANSIT_UNSET; +import static android.view.WindowManager.TRANSIT_UNSET; import static com.android.server.wm.TaskSnapshotController.*; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 5ed17ccc6d9b..35ca493e909f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -539,7 +539,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void showRecentApps(boolean fromHome) { + public void showRecentApps() { } diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 6468763440a5..5f44fb675330 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -17,14 +17,17 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -74,11 +77,11 @@ public class ZOrderingTests extends WindowTestsBase { return super.setRelativeLayer(sc, relativeTo, layer); } - int getLayer(SurfaceControl sc) { + private int getLayer(SurfaceControl sc) { return mLayersForControl.getOrDefault(sc, 0); } - SurfaceControl getRelativeLayer(SurfaceControl sc) { + private SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } }; @@ -146,8 +149,9 @@ public class ZOrderingTests extends WindowTestsBase { return p; } - void assertZOrderGreaterThan(LayerRecordingTransaction t, - SurfaceControl left, SurfaceControl right) throws Exception { + + void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, + SurfaceControl right) throws Exception { final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left); final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right); @@ -171,9 +175,12 @@ public class ZOrderingTests extends WindowTestsBase { } } - void assertWindowLayerGreaterThan(LayerRecordingTransaction t, - WindowState left, WindowState right) throws Exception { - assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl()); + void assertWindowHigher(WindowState left, WindowState right) throws Exception { + assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); + } + + WindowState createWindow(String name) { + return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); } @Test @@ -184,38 +191,37 @@ public class ZOrderingTests extends WindowTestsBase { // The Ime has an higher base layer than app windows and lower base layer than system // windows, so it should be above app windows and below system windows if there isn't an IME // target. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); sWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, "imeAppTargetChildAboveWindow"); @@ -228,41 +234,38 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows except for child windows that are z-ordered above it // and below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception { - final WindowState appBelowImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget"); - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); - final WindowState appAboveImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget"); + final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); sWm.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and // below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget); - assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, appBelowImeTarget); + assertWindowHigher(appAboveImeTarget, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -276,20 +279,20 @@ public class ZOrderingTests extends WindowTestsBase { // The IME target base layer is higher than all window except for the nav bar window, so the // IME should be above all windows except for the nav bar. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, imeSystemOverlayTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this // test enforced. However this seems like the wrong behavior unless the status bar is the // IME target. - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -297,17 +300,18 @@ public class ZOrderingTests extends WindowTestsBase { sWm.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testStackLayers() throws Exception { + final WindowState anyWindow1 = createWindow("anyWindow"); final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); @@ -317,12 +321,22 @@ public class ZOrderingTests extends WindowTestsBase { final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow"); + final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, + mDisplayContent, "homeActivityWindow"); + final WindowState anyWindow2 = createWindow("anyWindow2"); mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); - assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); + assertWindowHigher(dockedStackWindow, homeActivityWindow); + assertWindowHigher(assistantStackWindow, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, homeActivityWindow); + assertWindowHigher(anyWindow1, homeActivityWindow); + assertWindowHigher(anyWindow2, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, anyWindow1); + assertWindowHigher(pinnedStackWindow, anyWindow2); + assertWindowHigher(pinnedStackWindow, dockedStackWindow); + assertWindowHigher(pinnedStackWindow, assistantStackWindow); } @Test @@ -337,9 +351,9 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel); + assertWindowHigher(navBarPanel, mNavBarWindow); + assertWindowHigher(statusBarPanel, mStatusBarWindow); + assertWindowHigher(statusBarSubPanel, statusBarPanel); } @Test @@ -347,8 +361,7 @@ public class ZOrderingTests extends WindowTestsBase { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. - final WindowState anyWindow = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); + final WindowState anyWindow = createWindow("anyWindow"); final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, "TypeApplicationMediaChild"); final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY, @@ -356,7 +369,29 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild); - assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child); + assertWindowHigher(anyWindow, mediaOverlayChild); + assertWindowHigher(mediaOverlayChild, child); + } + + @Test + public void testDockedDividerPosition() throws Exception { + final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, + "pinnedStackWindow"); + final WindowState splitScreenWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); + final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, + mDisplayContent, "assistantStackWindow"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(mDockedDividerWindow, splitScreenWindow); + assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); + assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); } } diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index f022dcf376a6..347557291427 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -25,6 +25,7 @@ <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java index 3dcd5b9829da..30fae01f3036 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java @@ -78,6 +78,8 @@ public class NotificationComparatorTest extends UiServiceTestCase { private NotificationRecord mRecordCheater; private NotificationRecord mRecordCheaterColorized; private NotificationRecord mNoMediaSessionMedia; + private NotificationRecord mRecordColorized; + private NotificationRecord mRecordColorizedCall; @Before public void setUp() { @@ -113,7 +115,6 @@ public class NotificationComparatorTest extends UiServiceTestCase { Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) - .setColorized(true /* colorized */) .build(); mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, callPkg, 1, "highcall", callUid, callUid, n2, @@ -200,13 +201,34 @@ public class NotificationComparatorTest extends UiServiceTestCase { pkg2, pkg2, 1, "cheater", uid2, uid2, n12, new UserHandle(userId), "", 9258), getDefaultChannel()); mNoMediaSessionMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT); + + Notification n13 = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .setColorized(true /* colorized */) + .build(); + mRecordColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2, + pkg2, 1, "colorized", uid2, uid2, n13, + new UserHandle(userId), "", 1999), getDefaultChannel()); + mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH); + + Notification n14 = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setCategory(Notification.CATEGORY_CALL) + .setColorized(true) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .build(); + mRecordColorizedCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg, + callPkg, 1, "colorizedCall", callUid, callUid, n14, + new UserHandle(userId), "", 1999), getDefaultChannel()); + mRecordColorizedCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH); } @Test public void testOrdering() throws Exception { final List<NotificationRecord> expected = new ArrayList<>(); - expected.add(mRecordHighCall); + expected.add(mRecordColorizedCall); expected.add(mRecordDefaultMedia); + expected.add(mRecordColorized); + expected.add(mRecordHighCall); expected.add(mRecordInlineReply); expected.add(mRecordSms); expected.add(mRecordStarredContact); diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java index ce328c29f01c..aada68273af0 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java @@ -149,7 +149,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { ISliceListener listener = mock(ISliceListener.class); assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); assertTrue(mPinnedSliceManager.isPinned()); assertTrue(mPinnedSliceManager.removeSliceListener(listener)); @@ -162,9 +162,9 @@ public class PinnedSliceStateTest extends UiServiceTestCase { ISliceListener listener2 = mock(ISliceListener.class); assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); assertTrue(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener2, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS); assertFalse(mPinnedSliceManager.removeSliceListener(listener)); assertTrue(mPinnedSliceManager.removeSliceListener(listener2)); @@ -176,7 +176,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { ISliceListener listener = mock(ISliceListener.class); assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); assertTrue(mPinnedSliceManager.isPinned()); mPinnedSliceManager.pin("pkg", FIRST_SPECS); @@ -199,7 +199,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { assertFalse(mPinnedSliceManager.isPinned()); - mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS); + mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS); mPinnedSliceManager.onChange(); TestableLooper.get(this).processAllMessages(); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index ff3d58652aed..6782188c0f34 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -78,6 +78,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; @@ -90,6 +91,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; /** * Manages the standby state of an app, listening to various events. @@ -128,6 +130,11 @@ public class AppStandbyController { // Expiration time for predicted bucket private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR; + /** + * Indicates the maximum wait time for admin data to be available; + */ + private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000; + // To name the lock for stack traces static class Lock {} @@ -153,6 +160,8 @@ public class AppStandbyController { @GuardedBy("mActiveAdminApps") private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>(); + private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1); + // Messages for the handler static final int MSG_INFORM_LISTENERS = 3; static final int MSG_FORCE_IDLE_STATE = 4; @@ -895,6 +904,20 @@ public class AppStandbyController { } } + public void onAdminDataAvailable() { + mAdminDataAvailableLatch.countDown(); + } + + /** + * This will only ever be called once - during device boot. + */ + private void waitForAdminData() { + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) { + ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch, + WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data"); + } + } + Set<String> getActiveAdminAppsForTest(int userId) { synchronized (mActiveAdminApps) { return mActiveAdminApps.get(userId); @@ -1224,6 +1247,7 @@ public class AppStandbyController { case MSG_ONE_TIME_CHECK_IDLE_STATES: mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES); + waitForAdminData(); checkIdleStates(UserHandle.USER_ALL); break; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 78cc81f2bab6..979feaa5a165 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1030,5 +1030,10 @@ public class UsageStatsService extends SystemService implements public void setActiveAdminApps(Set<String> packageNames, int userId) { mAppStandby.setActiveAdminApps(packageNames, userId); } + + @Override + public void onAdminDataAvailable() { + mAppStandby.onAdminDataAvailable(); + } } } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index e0b6f610ab55..e633053800bc 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -2564,6 +2564,35 @@ public final class Telephony { public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers"); /** + * The {@code content://} style URL to be called from DevicePolicyManagerService, + * can manage DPC-owned APNs. + * @hide + */ + public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc"); + + /** + * The {@code content://} style URL to be called from Telephony to query APNs. + * When DPC-owned APNs are enforced, only DPC-owned APNs are returned, otherwise only + * non-DPC-owned APNs are returned. + * @hide + */ + public static final Uri FILTERED_URI = Uri.parse("content://telephony/carriers/filtered"); + + /** + * The {@code content://} style URL to be called from DevicePolicyManagerService + * or Telephony to manage whether DPC-owned APNs are enforced. + * @hide + */ + public static final Uri ENFORCE_MANAGED_URI = Uri.parse( + "content://telephony/carriers/enforce_managed"); + + /** + * The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced. + * @hide + */ + public static final String ENFORCE_KEY = "enforced"; + + /** * The default sort order for this table. */ public static final String DEFAULT_SORT_ORDER = "name ASC"; diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 4e1c15fa1251..38408fe3d0fd 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -126,14 +126,31 @@ public class SubscriptionInfo implements Parcelable { private UiccAccessRule[] mAccessRules; /** + * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID + * for an eUICC card. + */ + private String mCardId; + + /** + * @hide + */ + public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, + CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, + Bitmap icon, int mcc, int mnc, String countryIso) { + this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, + roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */, + null /* accessRules */, null /* accessRules */); + } + + /** * @hide */ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, - Bitmap icon, int mcc, int mnc, String countryIso) { + Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded, + @Nullable UiccAccessRule[] accessRules) { this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, - roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */, - null /* accessRules */); + roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */); } /** @@ -142,7 +159,7 @@ public class SubscriptionInfo implements Parcelable { public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName, CharSequence carrierName, int nameSource, int iconTint, String number, int roaming, Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded, - @Nullable UiccAccessRule[] accessRules) { + @Nullable UiccAccessRule[] accessRules, String cardId) { this.mId = id; this.mIccId = iccId; this.mSimSlotIndex = simSlotIndex; @@ -158,6 +175,7 @@ public class SubscriptionInfo implements Parcelable { this.mCountryIso = countryIso; this.mIsEmbedded = isEmbedded; this.mAccessRules = accessRules; + this.mCardId = cardId; } /** @@ -387,6 +405,14 @@ public class SubscriptionInfo implements Parcelable { return mAccessRules; } + /** + * @return the ID of the SIM card which contains the subscription. + * @hide + */ + public String getCardId() { + return this.mCardId; + } + public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() { @Override public SubscriptionInfo createFromParcel(Parcel source) { @@ -405,10 +431,11 @@ public class SubscriptionInfo implements Parcelable { Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source); boolean isEmbedded = source.readBoolean(); UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR); + String cardId = source.readString(); return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso, - isEmbedded, accessRules); + isEmbedded, accessRules, cardId); } @Override @@ -434,6 +461,7 @@ public class SubscriptionInfo implements Parcelable { mIconBitmap.writeToParcel(dest, flags); dest.writeBoolean(mIsEmbedded); dest.writeTypedArray(mAccessRules, flags); + dest.writeString(mCardId); } @Override @@ -459,11 +487,13 @@ public class SubscriptionInfo implements Parcelable { @Override public String toString() { String iccIdToPrint = givePrintableIccid(mIccId); + String cardIdToPrint = givePrintableIccid(mCardId); return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex + " displayName=" + mDisplayName + " carrierName=" + mCarrierName + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc + " mnc " + mMnc + " isEmbedded " + mIsEmbedded - + " accessRules " + Arrays.toString(mAccessRules) + "}"; + + " accessRules " + Arrays.toString(mAccessRules) + + " cardId=" + cardIdToPrint + "}"; } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 2fafdf5c4693..57f4cf28d90d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -16,6 +16,10 @@ package android.telephony; +import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED; +import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED; + +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -30,6 +34,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.INetworkPolicyManager; +import android.net.NetworkCapabilities; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -38,7 +43,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.util.DisplayMetrics; -import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ISub; @@ -280,6 +284,14 @@ public class SubscriptionManager { public static final String IS_EMBEDDED = "is_embedded"; /** + * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the + * current enabled profile on the card, while for eUICC card it is the EID of the card. + * <P>Type: TEXT (String)</P> + * @hide + */ + public static final String CARD_ID = "card_id"; + + /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1. * <p>TYPE: BLOB @@ -499,7 +511,7 @@ public class SubscriptionManager { public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; private final Context mContext; - private final INetworkPolicyManager mNetworkPolicy; + private INetworkPolicyManager mNetworkPolicy; /** * A listener class for monitoring changes to {@link SubscriptionInfo} records. @@ -572,11 +584,9 @@ public class SubscriptionManager { } /** @hide */ - public SubscriptionManager(Context context) throws ServiceNotFoundException { + public SubscriptionManager(Context context) { if (DBG) logd("SubscriptionManager created"); mContext = context; - mNetworkPolicy = INetworkPolicyManager.Stub - .asInterface(ServiceManager.getServiceOrThrow(Context.NETWORK_POLICY_SERVICE)); } /** @@ -589,6 +599,14 @@ public class SubscriptionManager { .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); } + private final INetworkPolicyManager getNetworkPolicy() { + if (mNetworkPolicy == null) { + mNetworkPolicy = INetworkPolicyManager.Stub + .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + } + return mNetworkPolicy; + } + /** * 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 @@ -1686,7 +1704,7 @@ public class SubscriptionManager { public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) { try { SubscriptionPlan[] subscriptionPlans = - mNetworkPolicy.getSubscriptionPlans(subId, mContext.getOpPackageName()); + getNetworkPolicy().getSubscriptionPlans(subId, mContext.getOpPackageName()); return subscriptionPlans == null ? Collections.emptyList() : Arrays.asList(subscriptionPlans); } catch (RemoteException e) { @@ -1715,7 +1733,7 @@ public class SubscriptionManager { @SystemApi public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) { try { - mNetworkPolicy.setSubscriptionPlans(subId, + getNetworkPolicy().setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1725,7 +1743,76 @@ public class SubscriptionManager { /** @hide */ private String getSubscriptionPlansOwner(int subId) { try { - return mNetworkPolicy.getSubscriptionPlansOwner(subId); + return getNetworkPolicy().getSubscriptionPlansOwner(subId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered unmetered. This will be reflected + * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideUnmetered set if the billing relationship should be + * considered unmetered. + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @hide + */ + @SystemApi + public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, + @DurationMillisLong long timeoutMillis) { + try { + final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0; + mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue, + timeoutMillis, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered congested. This will cause the + * device to delay certain network requests when possible, such as developer + * jobs that are willing to run in a flexible time window. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideCongested set if the subscription should be considered + * congested. + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + * @hide + */ + @SystemApi + public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, + @DurationMillisLong long timeoutMillis) { + try { + final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0; + mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue, + timeoutMillis, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8edc8b17c9e0..de9e691eadcf 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2122,6 +2122,27 @@ public class TelephonyManager { * carrier restrictions. */ public static final int SIM_STATE_CARD_RESTRICTED = 9; + /** + * SIM card state: Loaded: SIM card applications have been loaded + * @hide + */ + @SystemApi + public static final int SIM_STATE_LOADED = 10; + /** + * SIM card state: SIM Card is present + * @hide + */ + @SystemApi + public static final int SIM_STATE_PRESENT = 11; + + /** + * Extra included in {@link Intent.ACTION_SIM_CARD_STATE_CHANGED} and + * {@link Intent.ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state. + * + * @hide + */ + @SystemApi + public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; /** * @return true if a ICC card is present @@ -2168,6 +2189,14 @@ public class TelephonyManager { * @see #SIM_STATE_CARD_RESTRICTED */ public int getSimState() { + int simState = getSimStateIncludingLoaded(); + if (simState == SIM_STATE_LOADED) { + simState = SIM_STATE_READY; + } + return simState; + } + + private int getSimStateIncludingLoaded() { int slotIndex = getSlotIndex(); // slotIndex may be invalid due to sim being absent. In that case query all slots to get // sim state @@ -2186,7 +2215,63 @@ public class TelephonyManager { "state as absent"); return SIM_STATE_ABSENT; } - return getSimState(slotIndex); + return SubscriptionManager.getSimStateForSlotIndex(slotIndex); + } + + /** + * Returns a constant indicating the state of the default SIM card. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_ABSENT + * @see #SIM_STATE_CARD_IO_ERROR + * @see #SIM_STATE_CARD_RESTRICTED + * @see #SIM_STATE_PRESENT + * + * @hide + */ + @SystemApi + public int getSimCardState() { + int simCardState = getSimState(); + switch (simCardState) { + case SIM_STATE_UNKNOWN: + case SIM_STATE_ABSENT: + case SIM_STATE_CARD_IO_ERROR: + case SIM_STATE_CARD_RESTRICTED: + return simCardState; + default: + return SIM_STATE_PRESENT; + } + } + + /** + * Returns a constant indicating the state of the card applications on the default SIM card. + * + * @see #SIM_STATE_UNKNOWN + * @see #SIM_STATE_PIN_REQUIRED + * @see #SIM_STATE_PUK_REQUIRED + * @see #SIM_STATE_NETWORK_LOCKED + * @see #SIM_STATE_NOT_READY + * @see #SIM_STATE_PERM_DISABLED + * @see #SIM_STATE_LOADED + * + * @hide + */ + @SystemApi + public int getSimApplicationState() { + int simApplicationState = getSimStateIncludingLoaded(); + switch (simApplicationState) { + case SIM_STATE_UNKNOWN: + case SIM_STATE_ABSENT: + case SIM_STATE_CARD_IO_ERROR: + case SIM_STATE_CARD_RESTRICTED: + return SIM_STATE_UNKNOWN; + case SIM_STATE_READY: + // Ready is not a valid state anymore. The state that is broadcast goes from + // NOT_READY to either LOCKED or LOADED. + return SIM_STATE_NOT_READY; + default: + return simApplicationState; + } } /** @@ -2207,6 +2292,9 @@ public class TelephonyManager { */ public int getSimState(int slotIndex) { int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex); + if (simState == SIM_STATE_LOADED) { + simState = SIM_STATE_READY; + } return simState; } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java new file mode 100644 index 000000000000..ea0817551369 --- /dev/null +++ b/telephony/java/android/telephony/data/DataService.java @@ -0,0 +1,540 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.CallSuper; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.net.LinkProperties; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.AccessNetworkConstants; +import android.telephony.Rlog; +import android.telephony.SubscriptionManager; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.List; + +/** + * Base class of data service. Services that extend DataService must register the service in + * their AndroidManifest to be detected by the framework. They must be protected by the permission + * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow + * the following format: + * ... + * <service android:name=".xxxDataService" + * android:permission="android.permission.BIND_DATA_SERVICE" > + * <intent-filter> + * <action android:name="android.telephony.data.DataService" /> + * </intent-filter> + * </service> + * @hide + */ +@SystemApi +public abstract class DataService extends Service { + private static final String TAG = DataService.class.getSimpleName(); + + public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService"; + public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID"; + + private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE = 1; + private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL = 2; + private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL = 3; + private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN = 4; + private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE = 5; + private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST = 6; + private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED = 7; + private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED = 8; + private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 9; + + private final HandlerThread mHandlerThread; + + private final DataServiceHandler mHandler; + + private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>(); + + private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>(); + + /** + * The abstract class of the actual data service implementation. The data service provider + * must extend this class to support data connection. Note that each instance of data service + * provider is associated with one physical SIM slot. + */ + public class DataServiceProvider { + + private final int mSlotId; + + private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>(); + + /** + * Constructor + * @param slotId SIM slot id the data service provider associated with. + */ + public DataServiceProvider(int slotId) { + mSlotId = slotId; + } + + /** + * @return SIM slot id the data service provider associated with. + */ + public final int getSlotId() { + return mSlotId; + } + + /** + * Setup a data connection. The data service provider must implement this method to support + * establishing a packet data connection. When completed or error, the service must invoke + * the provided callback to notify the platform. + * + * @param accessNetworkType Access network type that the data call will be established on. + * Must be one of {@link AccessNetworkConstants.AccessNetworkType}. + * @param dataProfile Data profile used for data call setup. See {@link DataProfile} + * @param isRoaming True if the device is data roaming. + * @param allowRoaming True if data roaming is allowed by the user. + * @param isHandover True if the request is for IWLAN handover. + * @param linkProperties If {@code isHandover} is true, this is the link properties of the + * existing data connection, otherwise null. + * @param callback The result callback for this request. + */ + public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, + boolean allowRoaming, boolean isHandover, + LinkProperties linkProperties, DataServiceCallback callback) { + // The default implementation is to return unsupported. + callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null); + } + + /** + * Deactivate a data connection. The data service provider must implement this method to + * support data connection tear down. When completed or error, the service must invoke the + * provided callback to notify the platform. + * + * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall( + * int, DataProfile, boolean, boolean, boolean, LinkProperties, DataServiceCallback)}. + * @param reasonRadioShutDown True if the deactivate request reason is device shut down. + * @param isHandover True if the request is for IWLAN handover. + * @param callback The result callback for this request. + */ + public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover, + DataServiceCallback callback) { + // The default implementation is to return unsupported. + callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + + /** + * Set an APN to initial attach network. + * + * @param dataProfile Data profile used for data call setup. See {@link DataProfile}. + * @param isRoaming True if the device is data roaming. + * @param callback The result callback for this request. + */ + public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, + DataServiceCallback callback) { + // The default implementation is to return unsupported. + callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + + /** + * Send current carrier's data profiles to the data service for data call setup. This is + * only for CDMA carrier that can change the profile through OTA. The data service should + * always uses the latest data profile sent by the framework. + * + * @param dps A list of data profiles. + * @param isRoaming True if the device is data roaming. + * @param callback The result callback for this request. + */ + public void setDataProfile(List<DataProfile> dps, boolean isRoaming, + DataServiceCallback callback) { + // The default implementation is to return unsupported. + callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); + } + + /** + * Get the active data call list. + * + * @param callback The result callback for this request. + */ + public void getDataCallList(DataServiceCallback callback) { + // The default implementation is to return unsupported. + callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null); + } + + private void registerForDataCallListChanged(IDataServiceCallback callback) { + synchronized (mDataCallListChangedCallbacks) { + mDataCallListChangedCallbacks.add(callback); + } + } + + private void unregisterForDataCallListChanged(IDataServiceCallback callback) { + synchronized (mDataCallListChangedCallbacks) { + mDataCallListChangedCallbacks.remove(callback); + } + } + + /** + * Notify the system that current data call list changed. Data service must invoke this + * method whenever there is any data call status changed. + * + * @param dataCallList List of the current active data call. + */ + public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) { + synchronized (mDataCallListChangedCallbacks) { + for (IDataServiceCallback callback : mDataCallListChangedCallbacks) { + mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId, + 0, new DataCallListChangedIndication(dataCallList, callback)) + .sendToTarget(); + } + } + } + + /** + * Called when the instance of data service is destroyed (e.g. got unbind or binder died). + */ + @CallSuper + protected void onDestroy() { + mDataCallListChangedCallbacks.clear(); + } + } + + private static final class SetupDataCallRequest { + public final int accessNetworkType; + public final DataProfile dataProfile; + public final boolean isRoaming; + public final boolean allowRoaming; + public final boolean isHandover; + public final LinkProperties linkProperties; + public final IDataServiceCallback callback; + SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, + boolean allowRoaming, boolean isHandover, + LinkProperties linkProperties, IDataServiceCallback callback) { + this.accessNetworkType = accessNetworkType; + this.dataProfile = dataProfile; + this.isRoaming = isRoaming; + this.allowRoaming = allowRoaming; + this.linkProperties = linkProperties; + this.isHandover = isHandover; + this.callback = callback; + } + } + + private static final class DeactivateDataCallRequest { + public final int cid; + public final boolean reasonRadioShutDown; + public final boolean isHandover; + public final IDataServiceCallback callback; + DeactivateDataCallRequest(int cid, boolean reasonRadioShutDown, boolean isHandover, + IDataServiceCallback callback) { + this.cid = cid; + this.reasonRadioShutDown = reasonRadioShutDown; + this.isHandover = isHandover; + this.callback = callback; + } + } + + private static final class SetInitialAttachApnRequest { + public final DataProfile dataProfile; + public final boolean isRoaming; + public final IDataServiceCallback callback; + SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming, + IDataServiceCallback callback) { + this.dataProfile = dataProfile; + this.isRoaming = isRoaming; + this.callback = callback; + } + } + + private static final class SetDataProfileRequest { + public final List<DataProfile> dps; + public final boolean isRoaming; + public final IDataServiceCallback callback; + SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming, + IDataServiceCallback callback) { + this.dps = dps; + this.isRoaming = isRoaming; + this.callback = callback; + } + } + + private static final class DataCallListChangedIndication { + public final List<DataCallResponse> dataCallList; + public final IDataServiceCallback callback; + DataCallListChangedIndication(List<DataCallResponse> dataCallList, + IDataServiceCallback callback) { + this.dataCallList = dataCallList; + this.callback = callback; + } + } + + private class DataServiceHandler extends Handler { + + DataServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + IDataServiceCallback callback; + final int slotId = message.arg1; + DataServiceProvider service; + + synchronized (mServiceMap) { + service = mServiceMap.get(slotId); + } + + switch (message.what) { + case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE: + service = createDataServiceProvider(message.arg1); + if (service != null) { + mServiceMap.put(slotId, service); + } + break; + case DATA_SERVICE_REQUEST_SETUP_DATA_CALL: + if (service == null) break; + SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj; + service.setupDataCall(setupDataCallRequest.accessNetworkType, + setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, + setupDataCallRequest.allowRoaming, setupDataCallRequest.isHandover, + setupDataCallRequest.linkProperties, + new DataServiceCallback(setupDataCallRequest.callback)); + + break; + case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL: + if (service == null) break; + DeactivateDataCallRequest deactivateDataCallRequest = + (DeactivateDataCallRequest) message.obj; + service.deactivateDataCall(deactivateDataCallRequest.cid, + deactivateDataCallRequest.reasonRadioShutDown, + deactivateDataCallRequest.isHandover, + new DataServiceCallback(deactivateDataCallRequest.callback)); + break; + case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN: + if (service == null) break; + SetInitialAttachApnRequest setInitialAttachApnRequest = + (SetInitialAttachApnRequest) message.obj; + service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile, + setInitialAttachApnRequest.isRoaming, + new DataServiceCallback(setInitialAttachApnRequest.callback)); + break; + case DATA_SERVICE_REQUEST_SET_DATA_PROFILE: + if (service == null) break; + SetDataProfileRequest setDataProfileRequest = + (SetDataProfileRequest) message.obj; + service.setDataProfile(setDataProfileRequest.dps, + setDataProfileRequest.isRoaming, + new DataServiceCallback(setDataProfileRequest.callback)); + break; + case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST: + if (service == null) break; + + service.getDataCallList(new DataServiceCallback( + (IDataServiceCallback) message.obj)); + break; + case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED: + if (service == null) break; + service.registerForDataCallListChanged((IDataServiceCallback) message.obj); + break; + case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED: + if (service == null) break; + callback = (IDataServiceCallback) message.obj; + service.unregisterForDataCallListChanged(callback); + break; + case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED: + if (service == null) break; + DataCallListChangedIndication indication = + (DataCallListChangedIndication) message.obj; + try { + indication.callback.onDataCallListChanged(indication.dataCallList); + } catch (RemoteException e) { + loge("Failed to call onDataCallListChanged. " + e); + } + break; + } + } + } + + private DataService() { + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + + mHandler = new DataServiceHandler(mHandlerThread.getLooper()); + log("Data service created"); + } + + /** + * Create the instance of {@link DataServiceProvider}. Data service provider must override + * this method to facilitate the creation of {@link DataServiceProvider} instances. The system + * will call this method after binding the data service for each active SIM slot id. + * + * @param slotId SIM slot id the data service associated with. + * @return Data service object + */ + public abstract DataServiceProvider createDataServiceProvider(int slotId); + + /** @hide */ + @Override + public IBinder onBind(Intent intent) { + if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) { + loge("Unexpected intent " + intent); + return null; + } + + int slotId = intent.getIntExtra( + DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX); + + if (!SubscriptionManager.isValidSlotIndex(slotId)) { + loge("Invalid slot id " + slotId); + return null; + } + + log("onBind: slot id=" + slotId); + + IDataServiceWrapper binder = mBinderMap.get(slotId); + if (binder == null) { + Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE); + msg.arg1 = slotId; + msg.sendToTarget(); + + binder = new IDataServiceWrapper(slotId); + mBinderMap.put(slotId, binder); + } + + return binder; + } + + /** @hide */ + @Override + public boolean onUnbind(Intent intent) { + int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID, + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + if (mBinderMap.get(slotId) != null) { + DataServiceProvider serviceImpl; + synchronized (mServiceMap) { + serviceImpl = mServiceMap.get(slotId); + } + if (serviceImpl != null) { + serviceImpl.onDestroy(); + } + mBinderMap.remove(slotId); + } + + // If all clients unbinds, quit the handler thread + if (mBinderMap.size() == 0) { + mHandlerThread.quit(); + } + + return false; + } + + /** @hide */ + @Override + public void onDestroy() { + synchronized (mServiceMap) { + for (int i = 0; i < mServiceMap.size(); i++) { + DataServiceProvider serviceImpl = mServiceMap.get(i); + if (serviceImpl != null) { + serviceImpl.onDestroy(); + } + } + mServiceMap.clear(); + } + + mHandlerThread.quit(); + } + + /** + * A wrapper around IDataService that forwards calls to implementations of {@link DataService}. + */ + private class IDataServiceWrapper extends IDataService.Stub { + + private final int mSlotId; + + IDataServiceWrapper(int slotId) { + mSlotId = slotId; + } + + @Override + public void setupDataCall(int accessNetworkType, DataProfile dataProfile, + boolean isRoaming, boolean allowRoaming, boolean isHandover, + LinkProperties linkProperties, IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0, + new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, + allowRoaming, isHandover, linkProperties, callback)) + .sendToTarget(); + } + + @Override + public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover, + IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0, + new DeactivateDataCallRequest(cid, reasonRadioShutDown, isHandover, callback)) + .sendToTarget(); + } + + @Override + public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, + IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0, + new SetInitialAttachApnRequest(dataProfile, isRoaming, callback)) + .sendToTarget(); + } + + @Override + public void setDataProfile(List<DataProfile> dps, boolean isRoaming, + IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0, + new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget(); + } + + @Override + public void getDataCallList(IDataServiceCallback callback) { + mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0, + callback).sendToTarget(); + } + + @Override + public void registerForDataCallListChanged(IDataServiceCallback callback) { + if (callback == null) { + loge("Callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId, + 0, callback).sendToTarget(); + } + + @Override + public void unregisterForDataCallListChanged(IDataServiceCallback callback) { + if (callback == null) { + loge("Callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId, + 0, callback).sendToTarget(); + } + } + + private void log(String s) { + Rlog.d(TAG, s); + } + + private void loge(String s) { + Rlog.e(TAG, s); + } +} diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java new file mode 100644 index 000000000000..b6a81f94028b --- /dev/null +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -0,0 +1,172 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.os.RemoteException; +import android.telephony.Rlog; +import android.telephony.data.DataService.DataServiceProvider; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.List; + +/** + * Data service callback, which is for bound data service to invoke for solicited and unsolicited + * response. The caller is responsible to create a callback object for each single asynchronous + * request. + * + * @hide + */ +@SystemApi +public class DataServiceCallback { + + private static final String mTag = DataServiceCallback.class.getSimpleName(); + + /** + * Result of data requests + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY, + RESULT_ERROR_ILLEGAL_STATE}) + public @interface Result {} + + /** Request is completed successfully */ + public static final int RESULT_SUCCESS = 0; + /** Request is not support */ + public static final int RESULT_ERROR_UNSUPPORTED = 1; + /** Request contains invalid arguments */ + public static final int RESULT_ERROR_INVALID_ARG = 2; + /** Service is busy */ + public static final int RESULT_ERROR_BUSY = 3; + /** Request sent in illegal state */ + public static final int RESULT_ERROR_ILLEGAL_STATE = 4; + + private final WeakReference<IDataServiceCallback> mCallback; + + /** @hide */ + public DataServiceCallback(IDataServiceCallback callback) { + mCallback = new WeakReference<>(callback); + } + + /** + * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int, + * DataProfile, boolean, boolean, boolean, DataServiceCallback)}. + * + * @param result The result code. Must be one of the {@link Result}. + * @param response Setup data call response. + */ + public void onSetupDataCallComplete(@Result int result, DataCallResponse response) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onSetupDataCallComplete(result, response); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote"); + } + } + } + + /** + * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int, + * boolean, boolean, DataServiceCallback)}. + * + * @param result The result code. Must be one of the {@link Result}. + */ + public void onDeactivateDataCallComplete(@Result int result) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onDeactivateDataCallComplete(result); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote"); + } + } + } + + /** + * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn( + * DataProfile, boolean, DataServiceCallback)}. + * + * @param result The result code. Must be one of the {@link Result}. + */ + public void onSetInitialAttachApnComplete(@Result int result) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onSetInitialAttachApnComplete(result); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote"); + } + } + } + + /** + * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List, + * boolean, DataServiceCallback)}. + * + * @param result The result code. Must be one of the {@link Result}. + */ + @SystemApi + public void onSetDataProfileComplete(@Result int result) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onSetDataProfileComplete(result); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote"); + } + } + } + + /** + * Called to indicate result for the request {@link DataServiceProvider#getDataCallList( + * DataServiceCallback)}. + * + * @param result The result code. Must be one of the {@link Result}. + * @param dataCallList List of the current active data connection. + */ + public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onGetDataCallListComplete(result, dataCallList); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote"); + } + } + } + + /** + * Called to indicate that data connection list changed. + * + * @param dataCallList List of the current active data connection. + */ + public void onDataCallListChanged(List<DataCallResponse> dataCallList) { + IDataServiceCallback callback = mCallback.get(); + if (callback != null) { + try { + callback.onDataCallListChanged(dataCallList); + } catch (RemoteException e) { + Rlog.e(mTag, "Failed to onDataCallListChanged on the remote"); + } + } + } +} diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl new file mode 100644 index 000000000000..4eaaa252da02 --- /dev/null +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.net.LinkProperties; +import android.telephony.data.DataProfile; +import android.telephony.data.IDataServiceCallback; + +/** + * {@hide} + */ +oneway interface IDataService +{ + void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming, + boolean allowRoaming, boolean isHandover, in LinkProperties linkProperties, + IDataServiceCallback callback); + void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover, + IDataServiceCallback callback); + void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming, + IDataServiceCallback callback); + void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback); + void getDataCallList(IDataServiceCallback callback); + void registerForDataCallListChanged(IDataServiceCallback callback); + void unregisterForDataCallListChanged(IDataServiceCallback callback); +} diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl new file mode 100644 index 000000000000..856185b2974f --- /dev/null +++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl @@ -0,0 +1,33 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.telephony.data.DataCallResponse; + +/** + * The call back interface + * @hide + */ +oneway interface IDataServiceCallback +{ + void onSetupDataCallComplete(int result, in DataCallResponse dataCallResponse); + void onDeactivateDataCallComplete(int result); + void onSetInitialAttachApnComplete(int result); + void onSetDataProfileComplete(int result); + void onGetDataCallListComplete(int result, in List<DataCallResponse> dataCallList); + void onDataCallListChanged(in List<DataCallResponse> dataCallList); +} diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java index 29849c1f9ccd..b9ed0059a39a 100644 --- a/telephony/java/android/telephony/euicc/EuiccCardManager.java +++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java @@ -15,14 +15,40 @@ */ package android.telephony.euicc; +import android.annotation.IntDef; +import android.annotation.Nullable; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.service.euicc.EuiccProfileInfo; import android.util.Log; +import com.android.internal.telephony.euicc.IAuthenticateServerCallback; +import com.android.internal.telephony.euicc.ICancelSessionCallback; +import com.android.internal.telephony.euicc.IDeleteProfileCallback; +import com.android.internal.telephony.euicc.IDisableProfileCallback; import com.android.internal.telephony.euicc.IEuiccCardController; import com.android.internal.telephony.euicc.IGetAllProfilesCallback; +import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback; +import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback; +import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback; +import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback; +import com.android.internal.telephony.euicc.IGetProfileCallback; +import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback; +import com.android.internal.telephony.euicc.IGetSmdsAddressCallback; +import com.android.internal.telephony.euicc.IListNotificationsCallback; +import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback; +import com.android.internal.telephony.euicc.IPrepareDownloadCallback; +import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback; +import com.android.internal.telephony.euicc.IResetMemoryCallback; +import com.android.internal.telephony.euicc.IRetrieveNotificationCallback; +import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback; +import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback; +import com.android.internal.telephony.euicc.ISetNicknameCallback; +import com.android.internal.telephony.euicc.ISwitchToProfileCallback; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * EuiccCardManager is the application interface to an eSIM card. @@ -34,6 +60,53 @@ import com.android.internal.telephony.euicc.IGetAllProfilesCallback; public class EuiccCardManager { private static final String TAG = "EuiccCardManager"; + /** Reason for canceling a profile download session */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "CANCEL_REASON_" }, value = { + CANCEL_REASON_END_USER_REJECTED, + CANCEL_REASON_POSTPONED, + CANCEL_REASON_TIMEOUT, + CANCEL_REASON_PPR_NOT_ALLOWED + }) + public @interface CancelReason {} + + /** + * The end user has rejected the download. The profile will be put into the error state and + * cannot be downloaded again without the operator's change. + */ + public static final int CANCEL_REASON_END_USER_REJECTED = 0; + + /** The download has been postponed and can be restarted later. */ + public static final int CANCEL_REASON_POSTPONED = 1; + + /** The download has been timed out and can be restarted later. */ + public static final int CANCEL_REASON_TIMEOUT = 2; + + /** + * The profile to be downloaded cannot be installed due to its policy rule is not allowed by + * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The + * download can be restarted later. + */ + public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; + + /** Options for resetting eUICC memory */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = { + RESET_OPTION_DELETE_OPERATIONAL_PROFILES, + RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, + RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS + }) + public @interface ResetOption {} + + /** Deletes all operational profiles. */ + public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; + + /** Deletes all field-loaded testing profiles. */ + public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 1 << 1; + + /** Resets the default SM-DP+ address. */ + public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2; + /** Result code of execution with no error. */ public static final int RESULT_OK = 0; @@ -69,7 +142,7 @@ public class EuiccCardManager { /** * Gets all the profiles on eUicc. * - * @param callback the callback to get the result code and all the profiles. + * @param callback The callback to get the result code and all the profiles. */ public void getAllProfiles(ResultCallback<EuiccProfileInfo[]> callback) { try { @@ -85,4 +158,506 @@ public class EuiccCardManager { throw e.rethrowFromSystemServer(); } } + + /** + * Gets the profile of the given iccid. + * + * @param iccid The iccid of the profile. + * @param callback The callback to get the result code and profile. + */ + public void getProfile(String iccid, ResultCallback<EuiccProfileInfo> callback) { + try { + getIEuiccCardController().getProfile(mContext.getOpPackageName(), iccid, + new IGetProfileCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccProfileInfo profile) { + callback.onComplete(resultCode, profile); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getProfile", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Disables the profile of the given iccid. + * + * @param iccid The iccid of the profile. + * @param refresh Whether sending the REFRESH command to modem. + * @param callback The callback to get the result code. + */ + public void disableProfile(String iccid, boolean refresh, ResultCallback<Void> callback) { + try { + getIEuiccCardController().disableProfile(mContext.getOpPackageName(), iccid, refresh, + new IDisableProfileCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling disableProfile", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Switches from the current profile to another profile. The current profile will be disabled + * and the specified profile will be enabled. + * + * @param iccid The iccid of the profile to switch to. + * @param refresh Whether sending the REFRESH command to modem. + * @param callback The callback to get the result code and the EuiccProfileInfo enabled. + */ + public void switchToProfile(String iccid, boolean refresh, + ResultCallback<EuiccProfileInfo> callback) { + try { + getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), iccid, refresh, + new ISwitchToProfileCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccProfileInfo profile) { + callback.onComplete(resultCode, profile); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling switchToProfile", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the EID of the eUICC. + * + * @return The EID. + */ + public String getEid() { + try { + return getIEuiccCardController().getEid(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getEid", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the nickname of the profile of the given iccid. + * + * @param iccid The iccid of the profile. + * @param nickname The nickname of the profile. + * @param callback The callback to get the result code. + */ + public void setNickname(String iccid, String nickname, ResultCallback<Void> callback) { + try { + getIEuiccCardController().setNickname(mContext.getOpPackageName(), iccid, nickname, + new ISetNicknameCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling setNickname", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Deletes the profile of the given iccid from eUICC. + * + * @param iccid The iccid of the profile. + * @param callback The callback to get the result code. + */ + public void deleteProfile(String iccid, ResultCallback<Void> callback) { + try { + getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), iccid, + new IDeleteProfileCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling deleteProfile", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Resets the eUICC memory. + * + * @param options Bits of the options of resetting which parts of the eUICC memory. See + * EuiccCard for details. + * @param callback The callback to get the result code. + */ + public void resetMemory(@ResetOption int options, ResultCallback<Void> callback) { + try { + getIEuiccCardController().resetMemory(mContext.getOpPackageName(), options, + new IResetMemoryCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling resetMemory", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the default SM-DP+ address from eUICC. + * + * @param callback The callback to get the result code and the default SM-DP+ address. + */ + public void getDefaultSmdpAddress(ResultCallback<String> callback) { + try { + getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), + new IGetDefaultSmdpAddressCallback.Stub() { + @Override + public void onComplete(int resultCode, String address) { + callback.onComplete(resultCode, address); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getDefaultSmdpAddress", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the SM-DS address from eUICC. + * + * @param callback The callback to get the result code and the SM-DS address. + */ + public void getSmdsAddress(ResultCallback<String> callback) { + try { + getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), + new IGetSmdsAddressCallback.Stub() { + @Override + public void onComplete(int resultCode, String address) { + callback.onComplete(resultCode, address); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getSmdsAddress", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the default SM-DP+ address of eUICC. + * + * @param defaultSmdpAddress The default SM-DP+ address to set. + * @param callback The callback to get the result code. + */ + public void setDefaultSmdpAddress(String defaultSmdpAddress, ResultCallback<Void> callback) { + try { + getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), + defaultSmdpAddress, + new ISetDefaultSmdpAddressCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling setDefaultSmdpAddress", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets Rules Authorisation Table. + * + * @param callback the callback to get the result code and the rule authorisation table. + */ + public void getRulesAuthTable(ResultCallback<EuiccRulesAuthTable> callback) { + try { + getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), + new IGetRulesAuthTableCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccRulesAuthTable rat) { + callback.onComplete(resultCode, rat); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getRulesAuthTable", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the eUICC challenge for new profile downloading. + * + * @param callback the callback to get the result code and the challenge. + */ + public void getEuiccChallenge(ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), + new IGetEuiccChallengeCallback.Stub() { + @Override + public void onComplete(int resultCode, byte[] challenge) { + callback.onComplete(resultCode, challenge); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getEuiccChallenge", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading. + * + * @param callback the callback to get the result code and the info1. + */ + public void getEuiccInfo1(ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), + new IGetEuiccInfo1Callback.Stub() { + @Override + public void onComplete(int resultCode, byte[] info) { + callback.onComplete(resultCode, info); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getEuiccInfo1", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading. + * + * @param callback the callback to get the result code and the info2. + */ + public void getEuiccInfo2(ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), + new IGetEuiccInfo2Callback.Stub() { + @Override + public void onComplete(int resultCode, byte[] info) { + callback.onComplete(resultCode, info); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling getEuiccInfo2", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Authenticates the SM-DP+ server by the eUICC. + * + * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not + * required. + * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server. + * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is + * returned by SM-DP+ server. + * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be + * used by the eUICC for signature which is returned by SM-DP+ server. This is defined in + * GSMA RSP v2.0+. + * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by + * SM-DP+ server. + * @param callback the callback to get the result code and a byte array which represents a + * {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+. + */ + public void authenticateServer(String matchingId, byte[] serverSigned1, + byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate, + ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().authenticateServer( + mContext.getOpPackageName(), + matchingId, + serverSigned1, + serverSignature1, + euiccCiPkIdToBeUsed, + serverCertificate, + new IAuthenticateServerCallback.Stub() { + @Override + public void onComplete(int resultCode, byte[] response) { + callback.onComplete(resultCode, response); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling authenticateServer", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Prepares the profile download request sent to SM-DP+. + * + * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code + * required. + * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+ + * returned by SM-DP+ server. + * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by + * SM-DP+ server. + * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned + * by SM-DP+ server. + * @param callback the callback to get the result code and a byte array which represents a + * {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+ + */ + public void prepareDownload(@Nullable byte[] hashCc, byte[] smdpSigned2, + byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().prepareDownload( + mContext.getOpPackageName(), + hashCc, + smdpSigned2, + smdpSignature2, + smdpCertificate, + new IPrepareDownloadCallback.Stub() { + @Override + public void onComplete(int resultCode, byte[] response) { + callback.onComplete(resultCode, response); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling prepareDownload", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Loads a downloaded bound profile package onto the eUICC. + * + * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server. + * @param callback the callback to get the result code and a byte array which represents a + * {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+. + */ + public void loadBoundProfilePackage(byte[] boundProfilePackage, + ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().loadBoundProfilePackage( + mContext.getOpPackageName(), + boundProfilePackage, + new ILoadBoundProfilePackageCallback.Stub() { + @Override + public void onComplete(int resultCode, byte[] response) { + callback.onComplete(resultCode, response); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling loadBoundProfilePackage", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Cancels the current profile download session. + * + * @param transactionId the transaction ID returned by SM-DP+ server. + * @param reason the cancel reason. + * @param callback the callback to get the result code and an byte[] which represents a + * {@code CancelSessionResponse} defined in GSMA RSP v2.0+. + */ + public void cancelSession(byte[] transactionId, @CancelReason int reason, + ResultCallback<byte[]> callback) { + try { + getIEuiccCardController().cancelSession( + mContext.getOpPackageName(), + transactionId, + reason, + new ICancelSessionCallback.Stub() { + @Override + public void onComplete(int resultCode, byte[] response) { + callback.onComplete(resultCode, response); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling cancelSession", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Lists all notifications of the given {@code notificationEvents}. + * + * @param events bits of the event types ({@link EuiccNotification.Event}) to list. + * @param callback the callback to get the result code and the list of notifications. + */ + public void listNotifications(@EuiccNotification.Event int events, + ResultCallback<EuiccNotification[]> callback) { + try { + getIEuiccCardController().listNotifications(mContext.getOpPackageName(), events, + new IListNotificationsCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccNotification[] notifications) { + callback.onComplete(resultCode, notifications); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling listNotifications", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Retrieves contents of all notification of the given {@code events}. + * + * @param events bits of the event types ({@link EuiccNotification.Event}) to list. + * @param callback the callback to get the result code and the list of notifications. + */ + public void retrieveNotificationList(@EuiccNotification.Event int events, + ResultCallback<EuiccNotification[]> callback) { + try { + getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), events, + new IRetrieveNotificationListCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccNotification[] notifications) { + callback.onComplete(resultCode, notifications); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling retrieveNotificationList", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Retrieves the content of a notification of the given {@code seqNumber}. + * + * @param seqNumber the sequence number of the notification. + * @param callback the callback to get the result code and the notification. + */ + public void retrieveNotification(int seqNumber, ResultCallback<EuiccNotification> callback) { + try { + getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), seqNumber, + new IRetrieveNotificationCallback.Stub() { + @Override + public void onComplete(int resultCode, EuiccNotification notification) { + callback.onComplete(resultCode, notification); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling retrieveNotification", e); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes a notification from eUICC. + * + * @param seqNumber the sequence number of the notification. + * @param callback the callback to get the result code. + */ + public void removeNotificationFromList(int seqNumber, ResultCallback<Void> callback) { + try { + getIEuiccCardController().removeNotificationFromList( + mContext.getOpPackageName(), + seqNumber, + new IRemoveNotificationFromListCallback.Stub() { + @Override + public void onComplete(int resultCode) { + callback.onComplete(resultCode, null); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Error calling removeNotificationFromList", e); + throw e.rethrowFromSystemServer(); + } + } } diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 253432755179..d14670725e8e 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -71,8 +71,18 @@ public class EuiccManager { * TODO(b/35851809): Make this a SystemApi. */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_OTA_STATUS_CHANGED - = "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + public static final String ACTION_OTA_STATUS_CHANGED = + "android.telephony.euicc.action.OTA_STATUS_CHANGED"; + + /** + * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not + * completed. + * + * TODO(b/35851809): Make this a public API. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFY_CARRIER_SETUP = + "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP"; /** * Intent action to provision an embedded subscription. diff --git a/telephony/java/android/telephony/euicc/EuiccNotification.aidl b/telephony/java/android/telephony/euicc/EuiccNotification.aidl new file mode 100644 index 000000000000..dad770da260d --- /dev/null +++ b/telephony/java/android/telephony/euicc/EuiccNotification.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.euicc; + +parcelable EuiccNotification; diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl new file mode 100644 index 000000000000..9785a4533c5b --- /dev/null +++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.aidl @@ -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. + */ + +package android.telephony.euicc; + +parcelable EuiccRulesAuthTable;
\ No newline at end of file diff --git a/telephony/java/android/telephony/euicc/EuiccRat.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java index 6a56503ac380..7efe04364280 100644 --- a/telephony/java/android/telephony/euicc/EuiccRat.java +++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java @@ -35,7 +35,7 @@ import java.util.Arrays; * * TODO(b/35851809): Make this a @SystemApi. */ -public final class EuiccRat implements Parcelable { +public final class EuiccRulesAuthTable implements Parcelable { /** Profile policy rule flags */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = { @@ -50,7 +50,7 @@ public final class EuiccRat implements Parcelable { private final CarrierIdentifier[][] mCarrierIds; private final int[] mPolicyRuleFlags; - /** This is used to build new {@link EuiccRat} instance. */ + /** This is used to build new {@link EuiccRulesAuthTable} instance. */ public static final class Builder { private int[] mPolicyRules; private CarrierIdentifier[][] mCarrierIds; @@ -72,7 +72,7 @@ public final class EuiccRat implements Parcelable { * Builds the RAT instance. This builder should not be used anymore after this method is * called, otherwise {@link NullPointerException} will be thrown. */ - public EuiccRat build() { + public EuiccRulesAuthTable build() { if (mPosition != mPolicyRules.length) { throw new IllegalStateException( "Not enough rules are added, expected: " @@ -80,7 +80,7 @@ public final class EuiccRat implements Parcelable { + ", added: " + mPosition); } - return new EuiccRat(mPolicyRules, mCarrierIds, mPolicyRuleFlags); + return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags); } /** @@ -125,7 +125,8 @@ public final class EuiccRat implements Parcelable { return true; } - private EuiccRat(int[] policyRules, CarrierIdentifier[][] carrierIds, int[] policyRuleFlags) { + private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds, + int[] policyRuleFlags) { mPolicyRules = policyRules; mCarrierIds = carrierIds; mPolicyRuleFlags = policyRuleFlags; @@ -207,7 +208,7 @@ public final class EuiccRat implements Parcelable { return false; } - EuiccRat that = (EuiccRat) obj; + EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj; if (mCarrierIds.length != that.mCarrierIds.length) { return false; } @@ -234,7 +235,7 @@ public final class EuiccRat implements Parcelable { && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags); } - private EuiccRat(Parcel source) { + private EuiccRulesAuthTable(Parcel source) { mPolicyRules = source.createIntArray(); int len = mPolicyRules.length; mCarrierIds = new CarrierIdentifier[len][]; @@ -244,16 +245,16 @@ public final class EuiccRat implements Parcelable { mPolicyRuleFlags = source.createIntArray(); } - public static final Creator<EuiccRat> CREATOR = - new Creator<EuiccRat>() { + public static final Creator<EuiccRulesAuthTable> CREATOR = + new Creator<EuiccRulesAuthTable>() { @Override - public EuiccRat createFromParcel(Parcel source) { - return new EuiccRat(source); + public EuiccRulesAuthTable createFromParcel(Parcel source) { + return new EuiccRulesAuthTable(source); } @Override - public EuiccRat[] newArray(int size) { - return new EuiccRat[size]; + public EuiccRulesAuthTable[] newArray(int size) { + return new EuiccRulesAuthTable[size]; } }; } diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java index 4e095e3a7003..519710728403 100644 --- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java @@ -19,6 +19,7 @@ package android.telephony.ims.feature; import android.app.PendingIntent; import android.os.Message; import android.os.RemoteException; +import android.telephony.ims.internal.stub.SmsImplBase; import com.android.ims.ImsCallProfile; import com.android.ims.internal.IImsCallSession; @@ -28,6 +29,7 @@ import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsMMTelFeature; import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsSmsListener; import com.android.ims.internal.IImsUt; import com.android.ims.internal.ImsCallSession; @@ -171,6 +173,42 @@ public class MMTelFeature extends ImsFeature { return MMTelFeature.this.getMultiEndpointInterface(); } } + + @Override + public void setSmsListener(IImsSmsListener l) throws RemoteException { + synchronized (mLock) { + MMTelFeature.this.setSmsListener(l); + } + } + + @Override + public void sendSms(int token, int messageRef, String format, String smsc, boolean retry, + byte[] pdu) { + synchronized (mLock) { + MMTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu); + } + } + + @Override + public void acknowledgeSms(int token, int messageRef, int result) { + synchronized (mLock) { + MMTelFeature.this.acknowledgeSms(token, messageRef, result); + } + } + + @Override + public void acknowledgeSmsReport(int token, int messageRef, int result) { + synchronized (mLock) { + MMTelFeature.this.acknowledgeSmsReport(token, messageRef, result); + } + } + + @Override + public String getSmsFormat() { + synchronized (mLock) { + return MMTelFeature.this.getSmsFormat(); + } + } }; /** @@ -346,6 +384,39 @@ public class MMTelFeature extends ImsFeature { return null; } + public void setSmsListener(IImsSmsListener listener) { + getSmsImplementation().registerSmsListener(listener); + } + + public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, + byte[] pdu) { + getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu); + } + + public void acknowledgeSms(int token, int messageRef, + @SmsImplBase.DeliverStatusResult int result) { + getSmsImplementation().acknowledgeSms(token, messageRef, result); + } + + public void acknowledgeSmsReport(int token, int messageRef, + @SmsImplBase.StatusReportResult int result) { + getSmsImplementation().acknowledgeSmsReport(token, messageRef, result); + } + + /** + * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default + * non-functional implementation is returned. + * + * @return an instance of {@link SmsImplBase} which should be implemented by the IMS Provider. + */ + protected SmsImplBase getSmsImplementation() { + return new SmsImplBase(); + } + + public String getSmsFormat() { + return getSmsImplementation().getSmsFormat(); + } + @Override public void onFeatureReady() { diff --git a/telephony/java/android/telephony/ims/internal/SmsImplBase.java b/telephony/java/android/telephony/ims/internal/SmsImplBase.java index eb805a88da8e..33b23d94ad34 100644 --- a/telephony/java/android/telephony/ims/internal/SmsImplBase.java +++ b/telephony/java/android/telephony/ims/internal/SmsImplBase.java @@ -17,7 +17,6 @@ package android.telephony.ims.internal; import android.annotation.IntDef; -import android.annotation.SystemApi; import android.os.RemoteException; import android.telephony.SmsManager; import android.telephony.SmsMessage; @@ -37,6 +36,7 @@ import java.lang.annotation.RetentionPolicy; public class SmsImplBase { private static final String LOG_TAG = "SmsImplBase"; + /** @hide */ @IntDef({ SEND_STATUS_OK, SEND_STATUS_ERROR, @@ -68,6 +68,7 @@ public class SmsImplBase { */ public static final int SEND_STATUS_ERROR_FALLBACK = 4; + /** @hide */ @IntDef({ DELIVER_STATUS_OK, DELIVER_STATUS_ERROR @@ -84,6 +85,7 @@ public class SmsImplBase { */ public static final int DELIVER_STATUS_ERROR = 2; + /** @hide */ @IntDef({ STATUS_REPORT_STATUS_OK, STATUS_REPORT_STATUS_ERROR @@ -114,9 +116,9 @@ public class SmsImplBase { * @hide */ public final void registerSmsListener(IImsSmsListener listener) { - synchronized (mLock) { - mListener = listener; - } + synchronized (mLock) { + mListener = listener; + } } /** @@ -128,8 +130,8 @@ public class SmsImplBase { * callbacks for this specific message. * @param messageRef the message reference. * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. * @param smsc the Short Message Service Center address. - * {@link SmsMessage#FORMAT_3GPP2}. * @param isRetry whether it is a retry of an already attempted message or not. * @param pdu PDUs representing the contents of the message. */ @@ -154,7 +156,7 @@ public class SmsImplBase { * @param messageRef the message reference */ public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) { - + Log.e(LOG_TAG, "acknowledgeSms() not implemented."); } /** @@ -168,7 +170,7 @@ public class SmsImplBase { * @param messageRef the message reference */ public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) { - + Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented."); } /** @@ -193,7 +195,6 @@ public class SmsImplBase { } try { mListener.onSmsReceived(token, format, pdu); - acknowledgeSms(token, 0, DELIVER_STATUS_OK); } catch (RemoteException e) { Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage()); acknowledgeSms(token, 0, DELIVER_STATUS_ERROR); diff --git a/telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java b/telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java new file mode 100644 index 000000000000..113dad4696ef --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/SmsImplBase.java @@ -0,0 +1,271 @@ +/* + * 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.telephony.ims.internal.stub; + +import android.annotation.IntDef; +import android.os.RemoteException; +import android.telephony.SmsManager; +import android.telephony.SmsMessage; +import android.telephony.ims.internal.feature.MmTelFeature; +import android.util.Log; + +import com.android.ims.internal.IImsSmsListener; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for SMS over IMS. + * + * Any service wishing to provide SMS over IMS should extend this class and implement all methods + * that the service supports. + * @hide + */ +public class SmsImplBase { + private static final String LOG_TAG = "SmsImplBase"; + + @IntDef({ + SEND_STATUS_OK, + SEND_STATUS_ERROR, + SEND_STATUS_ERROR_RETRY, + SEND_STATUS_ERROR_FALLBACK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SendStatusResult {} + /** + * Message was sent successfully. + */ + public static final int SEND_STATUS_OK = 1; + + /** + * IMS provider failed to send the message and platform should not retry falling back to sending + * the message using the radio. + */ + public static final int SEND_STATUS_ERROR = 2; + + /** + * IMS provider failed to send the message and platform should retry again after setting TP-RD bit + * to high. + */ + public static final int SEND_STATUS_ERROR_RETRY = 3; + + /** + * IMS provider failed to send the message and platform should retry falling back to sending + * the message using the radio. + */ + public static final int SEND_STATUS_ERROR_FALLBACK = 4; + + @IntDef({ + DELIVER_STATUS_OK, + DELIVER_STATUS_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeliverStatusResult {} + /** + * Message was delivered successfully. + */ + public static final int DELIVER_STATUS_OK = 1; + + /** + * Message was not delivered. + */ + public static final int DELIVER_STATUS_ERROR = 2; + + @IntDef({ + STATUS_REPORT_STATUS_OK, + STATUS_REPORT_STATUS_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StatusReportResult {} + + /** + * Status Report was set successfully. + */ + public static final int STATUS_REPORT_STATUS_OK = 1; + + /** + * Error while setting status report. + */ + public static final int STATUS_REPORT_STATUS_ERROR = 2; + + + // Lock for feature synchronization + private final Object mLock = new Object(); + private IImsSmsListener mListener; + + /** + * Registers a listener responsible for handling tasks like delivering messages. + * + * @param listener listener to register. + * + * @hide + */ + public final void registerSmsListener(IImsSmsListener listener) { + synchronized (mLock) { + mListener = listener; + } + } + + /** + * This method will be triggered by the platform when the user attempts to send an SMS. This + * method should be implemented by the IMS providers to provide implementation of sending an SMS + * over IMS. + * + * @param token unique token generated by the platform that should be used when triggering + * callbacks for this specific message. + * @param messageRef the message reference. + * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. + * @param smsc the Short Message Service Center address. + * @param isRetry whether it is a retry of an already attempted message or not. + * @param pdu PDUs representing the contents of the message. + */ + public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, + byte[] pdu) { + // Base implementation returns error. Should be overridden. + try { + onSendSmsResult(token, messageRef, SEND_STATUS_ERROR, + SmsManager.RESULT_ERROR_GENERIC_FAILURE); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can not send sms: " + e.getMessage()); + } + } + + /** + * This method will be triggered by the platform after {@link #onSmsReceived(int, String, byte[])} + * has been called to deliver the result to the IMS provider. + * + * @param token token provided in {@link #onSmsReceived(int, String, byte[])} + * @param result result of delivering the message. Valid values are defined in + * {@link DeliverStatusResult} + * @param messageRef the message reference + */ + public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) { + Log.e(LOG_TAG, "acknowledgeSms() not implemented."); + } + + /** + * This method will be triggered by the platform after + * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the + * result to the IMS provider. + * + * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} + * @param result result of delivering the message. Valid values are defined in + * {@link StatusReportResult} + * @param messageRef the message reference + */ + public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) { + Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented."); + } + + /** + * This method should be triggered by the IMS providers when there is an incoming message. The + * platform will deliver the message to the messages database and notify the IMS provider of the + * result by calling {@link #acknowledgeSms(int, int, int)}. + * + * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called. + * + * @param token unique token generated by IMS providers that the platform will use to trigger + * callbacks for this message. + * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. + * @param pdu PDUs representing the contents of the message. + * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()} + */ + public final void onSmsReceived(int token, String format, byte[] pdu) + throws IllegalStateException { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Feature not ready."); + } + try { + mListener.onSmsReceived(token, format, pdu); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage()); + acknowledgeSms(token, 0, DELIVER_STATUS_ERROR); + } + } + } + + /** + * This method should be triggered by the IMS providers to pass the result of the sent message + * to the platform. + * + * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called. + * + * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} + * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040 + * @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult} + * @param reason reason in case status is failure. Valid values are: + * {@link SmsManager#RESULT_ERROR_NONE}, + * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE}, + * {@link SmsManager#RESULT_ERROR_RADIO_OFF}, + * {@link SmsManager#RESULT_ERROR_NULL_PDU}, + * {@link SmsManager#RESULT_ERROR_NO_SERVICE}, + * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED}, + * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED}, + * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED} + * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()} + * @throws RemoteException if the connection to the framework is not available. If this happens + * attempting to send the SMS should be aborted. + */ + public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status, + int reason) throws IllegalStateException, RemoteException { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Feature not ready."); + } + mListener.onSendSmsResult(token, messageRef, status, reason); + } + } + + /** + * Sets the status report of the sent message. + * + * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} + * @param messageRef the message reference. + * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. + * @param pdu PDUs representing the content of the status report. + * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()} + */ + public final void onSmsStatusReportReceived(int token, int messageRef, String format, + byte[] pdu) { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Feature not ready."); + } + try { + mListener.onSmsStatusReportReceived(token, messageRef, format, pdu); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); + acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR); + } + } + } + + /** + * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS + * Provider. + * + * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and + * {@link SmsMessage#FORMAT_3GPP2}. + */ + public String getSmsFormat() { + return SmsMessage.FORMAT_3GPP; + } +} diff --git a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl index 52b3853ec5b6..cce39f4daf2a 100644 --- a/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl +++ b/telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl @@ -25,6 +25,7 @@ import com.android.ims.internal.IImsConfig; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsMultiEndpoint; import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsSmsListener; import com.android.ims.internal.IImsUt; import android.os.Message; @@ -53,4 +54,11 @@ interface IImsMMTelFeature { IImsEcbm getEcbmInterface(); void setUiTTYMode(int uiTtyMode, in Message onComplete); IImsMultiEndpoint getMultiEndpointInterface(); + // SMS APIs + void setSmsListener(IImsSmsListener l); + oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry, + in byte[] pdu); + oneway void acknowledgeSms(int token, int messageRef, int result); + oneway void acknowledgeSmsReport(int token, int messageRef, int result); + String getSmsFormat(); } diff --git a/telephony/java/com/android/ims/internal/IImsSmsListener.aidl b/telephony/java/com/android/ims/internal/IImsSmsListener.aidl new file mode 100644 index 000000000000..5a4b7e489025 --- /dev/null +++ b/telephony/java/com/android/ims/internal/IImsSmsListener.aidl @@ -0,0 +1,28 @@ +/* + * 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.ims.internal; + +/** + * See SmsImplBase for more information. + * {@hide} + */ +interface IImsSmsListener { + void onSendSmsResult(int token, int messageRef, int status, int reason); + void onSmsStatusReportReceived(int token, int messageRef, in String format, + in byte[] pdu); + void onSmsReceived(int token, in String format, in byte[] pdu); +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java index f3d9335f4052..d57f9afa01e9 100644 --- a/telephony/java/com/android/internal/telephony/IccCardConstants.java +++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java @@ -30,16 +30,14 @@ public class IccCardConstants { public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY"; /* ABSENT means ICC is missing */ public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT"; + /* PRESENT means ICC is present */ + public static final String INTENT_VALUE_ICC_PRESENT = "PRESENT"; /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */ static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR"; /* CARD_RESTRICTED means card is present but not usable due to carrier restrictions */ static public final String INTENT_VALUE_ICC_CARD_RESTRICTED = "CARD_RESTRICTED"; /* LOCKED means ICC is locked by pin or by network */ public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED"; - //TODO: we can remove this state in the future if Bug 18489776 analysis - //#42's first race condition is resolved - /* INTERNAL LOCKED means ICC is locked by pin or by network */ - public static final String INTENT_VALUE_ICC_INTERNAL_LOCKED = "INTERNAL_LOCKED"; /* READY means ICC is ready to access */ public static final String INTENT_VALUE_ICC_READY = "READY"; /* IMSI means ICC IMSI is ready in property */ @@ -77,7 +75,8 @@ public class IccCardConstants { NOT_READY, /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */ PERM_DISABLED, /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */ CARD_IO_ERROR, /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */ - CARD_RESTRICTED;/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */ + CARD_RESTRICTED,/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */ + LOADED; /** ordinal(9) == {@See TelephonyManager#SIM_STATE_LOADED} */ public boolean isPinLocked() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); @@ -85,9 +84,9 @@ public class IccCardConstants { public boolean iccCardExist() { return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED) - || (this == NETWORK_LOCKED) || (this == READY) + || (this == NETWORK_LOCKED) || (this == READY) || (this == NOT_READY) || (this == PERM_DISABLED) || (this == CARD_IO_ERROR) - || (this == CARD_RESTRICTED)); + || (this == CARD_RESTRICTED) || (this == LOADED)); } public static State intToState(int state) throws IllegalArgumentException { @@ -102,6 +101,7 @@ public class IccCardConstants { case 7: return PERM_DISABLED; case 8: return CARD_IO_ERROR; case 9: return CARD_RESTRICTED; + case 10: return LOADED; default: throw new IllegalArgumentException(); } diff --git a/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl new file mode 100644 index 000000000000..8a77bf127f9e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IAuthenticateServerCallback { + void onComplete(int resultCode, in byte[] response); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl new file mode 100644 index 000000000000..f6b99e2b9801 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface ICancelSessionCallback { + void onComplete(int resultCode, in byte[] response); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl new file mode 100644 index 000000000000..23a642e8167f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IDeleteProfileCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl new file mode 100644 index 000000000000..3ee0b3a0fb69 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.service.euicc.EuiccProfileInfo; + +/** @hide */ +oneway interface IDisableProfileCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl index 2846a1ad1f9f..abc55c77cc1f 100644 --- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl +++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl @@ -17,8 +17,66 @@ package com.android.internal.telephony.euicc; import com.android.internal.telephony.euicc.IGetAllProfilesCallback; +import com.android.internal.telephony.euicc.IGetProfileCallback; +import com.android.internal.telephony.euicc.IDisableProfileCallback; +import com.android.internal.telephony.euicc.ISwitchToProfileCallback; +import com.android.internal.telephony.euicc.ISetNicknameCallback; +import com.android.internal.telephony.euicc.IDeleteProfileCallback; +import com.android.internal.telephony.euicc.IResetMemoryCallback; +import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback; +import com.android.internal.telephony.euicc.IGetSmdsAddressCallback; +import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback; +import com.android.internal.telephony.euicc.IAuthenticateServerCallback; +import com.android.internal.telephony.euicc.ICancelSessionCallback; +import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback; +import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback; +import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback; +import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback; +import com.android.internal.telephony.euicc.IListNotificationsCallback; +import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback; +import com.android.internal.telephony.euicc.IPrepareDownloadCallback; +import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback; +import com.android.internal.telephony.euicc.IRetrieveNotificationCallback; +import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback; /** @hide */ interface IEuiccCardController { oneway void getAllProfiles(String callingPackage, in IGetAllProfilesCallback callback); + oneway void getProfile(String callingPackage, String iccid, in IGetProfileCallback callback); + oneway void disableProfile(String callingPackage, String iccid, boolean refresh, + in IDisableProfileCallback callback); + oneway void switchToProfile(String callingPackage, String iccid, boolean refresh, + in ISwitchToProfileCallback callback); + String getEid(); + oneway void setNickname(String callingPackage, String iccid, String nickname, + in ISetNicknameCallback callback); + oneway void deleteProfile(String callingPackage, String iccid, + in IDeleteProfileCallback callback); + oneway void resetMemory(String callingPackage, int options, in IResetMemoryCallback callback); + oneway void getDefaultSmdpAddress(String callingPackage, + in IGetDefaultSmdpAddressCallback callback); + oneway void getSmdsAddress(String callingPackage, in IGetSmdsAddressCallback callback); + oneway void setDefaultSmdpAddress(String callingPackage, String address, + in ISetDefaultSmdpAddressCallback callback); + oneway void getRulesAuthTable(String callingPackage, in IGetRulesAuthTableCallback callback); + oneway void getEuiccChallenge(String callingPackage, in IGetEuiccChallengeCallback callback); + oneway void getEuiccInfo1(String callingPackage, in IGetEuiccInfo1Callback callback); + oneway void getEuiccInfo2(String callingPackage, in IGetEuiccInfo2Callback callback); + oneway void authenticateServer(String callingPackage, String matchingId, + in byte[] serverSigned1, in byte[] serverSignature1, in byte[] euiccCiPkIdToBeUsed, + in byte[] serverCertificatein, in IAuthenticateServerCallback callback); + oneway void prepareDownload(String callingPackage, in byte[] hashCc, in byte[] smdpSigned2, + in byte[] smdpSignature2, in byte[] smdpCertificate, in IPrepareDownloadCallback callback); + oneway void loadBoundProfilePackage(String callingPackage, in byte[] boundProfilePackage, + in ILoadBoundProfilePackageCallback callback); + oneway void cancelSession(String callingPackage, in byte[] transactionId, int reason, + in ICancelSessionCallback callback); + oneway void listNotifications(String callingPackage, int events, + in IListNotificationsCallback callback); + oneway void retrieveNotificationList(String callingPackage, int events, + in IRetrieveNotificationListCallback callback); + oneway void retrieveNotification(String callingPackage, int seqNumber, + in IRetrieveNotificationCallback callback); + oneway void removeNotificationFromList(String callingPackage, int seqNumber, + in IRemoveNotificationFromListCallback callback); } diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl new file mode 100644 index 000000000000..79b659d9316d --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IGetDefaultSmdpAddressCallback { + void onComplete(int resultCode, String address); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl new file mode 100644 index 000000000000..5ffb3400912a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IGetEuiccChallengeCallback { + void onComplete(int resultCode, in byte[] challenge); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl new file mode 100644 index 000000000000..9592acb6d330 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IGetEuiccInfo1Callback { + void onComplete(int resultCode, in byte[] info); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl new file mode 100644 index 000000000000..5256b35c7516 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IGetEuiccInfo2Callback { + void onComplete(int resultCode, in byte[] info); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl new file mode 100644 index 000000000000..e9fc9e9d5e6e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.service.euicc.EuiccProfileInfo; + +/** @hide */ +oneway interface IGetProfileCallback { + void onComplete(int resultCode, in EuiccProfileInfo profile); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl new file mode 100644 index 000000000000..58f0bde65b86 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.telephony.euicc.EuiccRulesAuthTable; + +/** @hide */ +oneway interface IGetRulesAuthTableCallback { + void onComplete(int resultCode, in EuiccRulesAuthTable rat); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl new file mode 100644 index 000000000000..09a83aab3200 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IGetSmdsAddressCallback { + void onComplete(int resultCode, String address); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl new file mode 100644 index 000000000000..65aa302a6a64 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.telephony.euicc.EuiccNotification; + +/** @hide */ +oneway interface IListNotificationsCallback { + void onComplete(int resultCode, in EuiccNotification[] notifications); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl new file mode 100644 index 000000000000..4ad7081c2e67 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface ILoadBoundProfilePackageCallback { + void onComplete(int resultCode, in byte[] response); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl new file mode 100644 index 000000000000..c0351841084e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IPrepareDownloadCallback { + void onComplete(int resultCode, in byte[] response); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl new file mode 100644 index 000000000000..b22d0da5dc5f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.telephony.euicc.EuiccNotification; + +/** @hide */ +oneway interface IRemoveNotificationFromListCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl new file mode 100644 index 000000000000..860c158e2b70 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface IResetMemoryCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl new file mode 100644 index 000000000000..dd8889a94143 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.telephony.euicc.EuiccNotification; + +/** @hide */ +oneway interface IRetrieveNotificationCallback { + void onComplete(int resultCode, in EuiccNotification notification); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl new file mode 100644 index 000000000000..bc4e451329af --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.telephony.euicc.EuiccNotification; + +/** @hide */ +oneway interface IRetrieveNotificationListCallback { + void onComplete(int resultCode, in EuiccNotification[] notifications); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl new file mode 100644 index 000000000000..1e47125123ee --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface ISetDefaultSmdpAddressCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl new file mode 100644 index 000000000000..589998069d92 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.telephony.euicc; + +/** @hide */ +oneway interface ISetNicknameCallback { + void onComplete(int resultCode); +} diff --git a/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl new file mode 100644 index 000000000000..21ff0848f65e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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 com.android.internal.telephony.euicc; + +import android.service.euicc.EuiccProfileInfo; + +/** @hide */ +oneway interface ISwitchToProfileCallback { + void onComplete(int resultCode, in EuiccProfileInfo profile); +} diff --git a/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java b/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java index 11ea36132dcc..4ab38a66d504 100644 --- a/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java +++ b/tests/UiBench/src/com/android/test/uibench/leanback/BrowseFragment.java @@ -24,6 +24,7 @@ public class BrowseFragment extends android.support.v17.leanback.app.BrowseSuppo @Override public void onCreate(Bundle savedInstanceState) { + TestHelper.initHeaderState(this); super.onCreate(savedInstanceState); BitmapLoader.clear(); TestHelper.initBackground(getActivity()); diff --git a/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java b/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java index 2bf388501ba0..bf408f7475ac 100644 --- a/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java +++ b/tests/UiBench/src/com/android/test/uibench/leanback/TestHelper.java @@ -40,6 +40,7 @@ public class TestHelper { public static final String EXTRA_CARD_ROUND_RECT = "extra_card_round_rect"; public static final String EXTRA_ENTRANCE_TRANSITION = "extra_entrance_transition"; public static final String EXTRA_BITMAP_UPLOAD = "extra_bitmap_upload"; + public static final String EXTRA_SHOW_FAST_LANE = "extra_show_fast_lane"; /** * Dont change the default values, they gave baseline for measuring the performance @@ -53,6 +54,7 @@ public class TestHelper { static final boolean DEFAULT_CARD_SHADOW = true; static final boolean DEFAULT_CARD_ROUND_RECT = true; static final boolean DEFAULT_BITMAP_UPLOAD = true; + static final boolean DEFAULT_SHOW_FAST_LANE = true; static long sCardIdSeed = 0; static long sRowIdSeed = 0; @@ -235,4 +237,11 @@ public class TestHelper { manager.setBitmap(bitmap); } } + + public static void initHeaderState(BrowseFragment fragment) { + if (!fragment.getActivity().getIntent() + .getBooleanExtra(EXTRA_SHOW_FAST_LANE, DEFAULT_SHOW_FAST_LANE)) { + fragment.setHeadersState(BrowseFragment.HEADERS_HIDDEN); + } + } } diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java index 52da79a18c6e..f3c22a51e267 100644 --- a/tests/net/java/android/net/LinkPropertiesTest.java +++ b/tests/net/java/android/net/LinkPropertiesTest.java @@ -79,6 +79,9 @@ public class LinkPropertiesTest { assertTrue(source.isIdenticalDnses(target)); assertTrue(target.isIdenticalDnses(source)); + assertTrue(source.isIdenticalPrivateDns(target)); + assertTrue(target.isIdenticalPrivateDns(source)); + assertTrue(source.isIdenticalRoutes(target)); assertTrue(target.isIdenticalRoutes(source)); @@ -91,6 +94,9 @@ public class LinkPropertiesTest { assertTrue(source.isIdenticalMtu(target)); assertTrue(target.isIdenticalMtu(source)); + assertTrue(source.isIdenticalTcpBufferSizes(target)); + assertTrue(target.isIdenticalTcpBufferSizes(source)); + // Check result of equals(). assertTrue(source.equals(target)); assertTrue(target.equals(source)); diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java index 25289ba92e24..035a4cd7601a 100644 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ b/tests/net/java/android/net/NetworkStatsTest.java @@ -16,6 +16,9 @@ package android.net; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.METERED_YES; @@ -56,71 +59,75 @@ public class NetworkStatsTest { @Test public void testFindIndex() throws Exception { final NetworkStats stats = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L, - 8L, 0L, 0L, 10) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L, - 1024L, 8L, 11) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 0L, 0L, - 1024L, 8L, 11) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L, - 8L, 1024L, 8L, 12) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L, - 8L, 1024L, 8L, 12); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) + .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12) + .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12); assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES)); + ROAMING_YES, DEFAULT_NETWORK_YES)); assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO)); + ROAMING_NO, DEFAULT_NETWORK_NO)); assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_NO)); + ROAMING_NO, DEFAULT_NETWORK_YES)); assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO)); + ROAMING_NO, DEFAULT_NETWORK_NO)); assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO)); + ROAMING_NO, DEFAULT_NETWORK_YES)); assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO)); + ROAMING_NO, DEFAULT_NETWORK_NO)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO)); } @Test public void testFindIndexHinted() { final NetworkStats stats = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L, - 8L, 0L, 0L, 10) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L, - 1024L, 8L, 11) - .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L, - 8L, 1024L, 8L, 12) + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 1024L, 8L, 0L, 0L, 10) - .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 0L, 0L, - 1024L, 8L, 11) - .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 0L, 0L, - 1024L, 8L, 11) - .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1024L, - 8L, 1024L, 8L, 12) - .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 1024L, - 8L, 1024L, 8L, 12); + DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10) + .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) + .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) + .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12); // verify that we correctly find across regardless of hinting for (int hint = 0; hint < stats.size(); hint++) { assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, - METERED_YES, ROAMING_NO, hint)); + METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, - METERED_YES, ROAMING_YES, hint)); + METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, hint)); assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, hint)); + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(-1, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, + METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, hint)); } } @@ -131,50 +138,50 @@ public class NetworkStatsTest { assertEquals(0, stats.size()); assertEquals(4, stats.internalSize()); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L, 1L, - 2L, 2L, 3); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 2L, 2L, - 2L, 2L, 4); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 3L, - 3L, 2L, 2L, 5); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 3L, - 3L, 2L, 2L, 5); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); assertEquals(4, stats.size()); assertEquals(4, stats.internalSize()); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4L, - 40L, 4L, 40L, 7); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5L, - 50L, 4L, 40L, 8); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 6L, - 60L, 5L, 50L, 10); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 7L, - 70L, 5L, 50L, 11); - stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, 7L, - 70L, 5L, 50L, 11); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); + stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); assertEquals(9, stats.size()); assertTrue(stats.internalSize() >= 9); assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 1L, 1L, 2L, 2L, 3); + DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 2L, 2L, 2L, 2L, 4); + DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - 3L, 3L, 2L, 2L, 5); + DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES, 3L, 3L, 2L, 2L, 5); + ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 4L, 40L, 4L, 40L, 7); + DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 5L, 50L, 4L, 40L, 8); + DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 6L, 60L, 5L, 50L, 10); + DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - 7L, 70L, 5L, 50L, 11); + DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES, 7L, 70L, 5L, 50L, 11); + ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); } @Test @@ -187,17 +194,17 @@ public class NetworkStatsTest { -128L, -1L, -1); assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 384L, 3L, 128L, 1L, 9); - assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, 128L, - 1L, 128L, 1L, 2); + DEFAULT_NETWORK_NO, 384L, 3L, 128L, 1L, 9); + assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 2); // now try combining that should create row stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 128L, 1L, 128L, 1L, 3); + DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 3); stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 256L, 2L, 256L, 2L, 6); + DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 6); } @Test @@ -213,10 +220,10 @@ public class NetworkStatsTest { final NetworkStats result = after.subtract(before); // identical data should result in zero delta - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, - 0L, 0L, 0L, 0); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, - 0L, 0L, 0L, 0); + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); } @Test @@ -232,10 +239,10 @@ public class NetworkStatsTest { final NetworkStats result = after.subtract(before); // expect delta between measurements - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1L, - 1L, 2L, 1L, 4); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 3L, - 1L, 4L, 1L, 8); + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1L, 1L, 2L, 1L, 4); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 3L, 1L, 4L, 1L, 8); } @Test @@ -252,12 +259,12 @@ public class NetworkStatsTest { final NetworkStats result = after.subtract(before); // its okay to have new rows - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, - 0L, 0L, 0L, 0); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 0L, - 0L, 0L, 0L, 0); + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 1024L, 8L, 1024L, 8L, 20); + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 20); } @Test @@ -274,7 +281,7 @@ public class NetworkStatsTest { // should silently drop omitted rows assertEquals(1, result.size()); assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 1L, 2L, 3L, 4L, 0); + ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 0); assertEquals(4L, result.getTotalBytes()); } @@ -301,21 +308,21 @@ public class NetworkStatsTest { assertEquals(64L, uidTag.getTotalBytes()); final NetworkStats uidMetered = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); assertEquals(96L, uidMetered.getTotalBytes()); final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 32L, 0L, - 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); assertEquals(96L, uidRoaming.getTotalBytes()); } @@ -331,38 +338,38 @@ public class NetworkStatsTest { @Test public void testGroupedByIfaceAll() throws Exception { final NetworkStats uidStats = new NetworkStats(TEST_START, 3) - .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, 0L, - 2L, 20L) - .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, 128L, - 8L, 0L, 2L, 20L) - .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, 128L, 8L, 0L, - 2L, 20L); + .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L) + .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L); final NetworkStats grouped = uidStats.groupedByIface(); assertEquals(3, uidStats.size()); assertEquals(1, grouped.size()); assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - 384L, 24L, 0L, 6L, 0L); + DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 6L, 0L); } @Test public void testGroupedByIface() throws Exception { final NetworkStats uidStats = new NetworkStats(TEST_START, 7) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, - 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L, - 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L, - 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L, - 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 128L, 8L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L, - 8L, 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) + .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L); final NetworkStats grouped = uidStats.groupedByIface(); @@ -370,59 +377,59 @@ public class NetworkStatsTest { assertEquals(2, grouped.size()); assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - 384L, 24L, 0L, 2L, 0L); + DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 2L, 0L); assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - 1024L, 64L, 0L, 0L, 0L); + DEFAULT_NETWORK_ALL, 1024L, 64L, 0L, 0L, 0L); } @Test public void testAddAllValues() { final NetworkStats first = new NetworkStats(TEST_START, 5) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 32L, - 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L, - 0L, 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); final NetworkStats second = new NetworkStats(TEST_START, 2) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 32L, 0L, - 0L, 0L, 0L) - .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 32L, - 0L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, 32L, - 0L, 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); first.combineAllValues(second); assertEquals(4, first.size()); - assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 64L, - 0L, 0L, 0L, 0L); + assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 32L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, - 64L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 32L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); } @Test public void testGetTotal() { final NetworkStats stats = new NetworkStats(TEST_START, 7) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, 8L, - 0L, 2L, 20L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 512L, - 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, 4L, - 0L, 0L, 0L) - .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 512L, - 32L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, 128L, - 8L, 0L, 0L, 0L) - .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L, 8L, - 0L, 0L, 0L) - .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, 128L, - 8L, 0L, 0L, 0L); + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) + .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) + .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L); assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L); @@ -449,9 +456,9 @@ public class NetworkStatsTest { assertEquals(6, before.size()); assertEquals(2, after.size()); assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 128L, 8L, 0L, 0L, 0L); - assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 128L, - 8L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); + assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); } @Test @@ -489,90 +496,90 @@ public class NetworkStatsTest { final String underlyingIface = "wlan0"; final int testTag1 = 8888; NetworkStats delta = new NetworkStats(TEST_START, 17) - .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 39605L, 46L, - 12259L, 55L, 0L) - .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L, - 0L, 0L, 0L) - .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 72667L, 197L, - 43909L, 241L, 0L) - .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 9297L, - 17L, 4128L, 21L, 0L) + .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L) + .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L) + .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L) // VPN package also uses some traffic through unprotected network. - .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 4983L, 10L, - 1801L, 12L, 0L) - .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, 0L, - 0L, 0L, 0L) + .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L) + .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) // Tag entries - .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, 21691L, 41L, - 13820L, 51L, 0L) - .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, 1281L, 2L, - 665L, 2L, 0L) + .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L) + .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L) // Irrelevant entries - .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1685L, 5L, - 2070L, 6L, 0L) + .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L) // Underlying Iface entries - .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 5178L, - 8L, 2139L, 11L, 0L) - .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, 0L, - 0L, 0L, 0L, 0L) + .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L) + .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 149873L, 287L, 59217L /* smaller than sum(tun0) */, + DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */, 299L /* smaller than sum(tun0) */, 0L) .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 0L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); + assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface)); assertEquals(20, delta.size()); // tunIface and TEST_IFACE entries are not changed. assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 39605L, 46L, 12259L, 55L, 0L); + DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L); assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 0L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 72667L, 197L, 43909L, 241L, 0L); + DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L); assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 9297L, 17L, 4128L, 21L, 0L); + DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L); assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 4983L, 10L, 1801L, 12L, 0L); + DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L); assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 0L, 0L, 0L, 0L, 0L); + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, - 21691L, 41L, 13820L, 51L, 0L); + DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L); assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, - 1281L, 2L, 665L, 2L, 0L); + DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L); assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 1685L, 5L, 2070L, 6L, 0L); + DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L); // Existing underlying Iface entries are updated assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 44783L, 54L, 14178L, 62L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 44783L, 54L, 14178L, 62L, 0L); assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, 0L, 0L, 0L, 0L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); // VPN underlying Iface entries are updated assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 28304L, 27L, 1L, 2L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 28304L, 27L, 1L, 2L, 0L); assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, 0L, 0L, 0L, 0L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); // New entries are added for new application's underlying Iface traffic assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 72667L, 197L, 43123L, 227L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 72667L, 197L, 43123L, 227L, 0L); assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, 9297L, 17L, 4054, 19L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 9297L, 17L, 4054, 19L, 0L); assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO, - ROAMING_NO, 21691L, 41L, 13572L, 48L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 21691L, 41L, 13572L, 48L, 0L); assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, - ROAMING_NO, 1281L, 2L, 653L, 1L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 1281L, 2L, 653L, 1L, 0L); // New entries are added for debug purpose assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, 39605L, 46L, 12039, 51, 0); + ROAMING_NO, DEFAULT_NETWORK_NO, 39605L, 46L, 12039, 51, 0); assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, 81964, 214, 47177, 246, 0); + ROAMING_NO, DEFAULT_NETWORK_NO, 81964, 214, 47177, 246, 0); assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, - ROAMING_ALL, 121569, 260, 59216, 297, 0); + ROAMING_ALL, DEFAULT_NETWORK_ALL, 121569, 260, 59216, 297, 0); } @@ -587,79 +594,80 @@ public class NetworkStatsTest { final String underlyingIface = "wlan0"; NetworkStats delta = new NetworkStats(TEST_START, 9) // 2 different apps sent/receive data via tun0. - .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50000L, 25L, - 100000L, 50L, 0L) - .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 500L, 2L, - 200L, 5L, 0L) + .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L) + .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L) // VPN package resends data through the tunnel (with exaggerated overhead) - .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 240000, - 100L, 120000L, 60L, 0L) + .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L) // 1 app already has some traffic on the underlying interface, the other doesn't yet - .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 1000L, - 10L, 2000L, 20L, 0L) + .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L) // Traffic through the underlying interface via the vpn app. // This test should redistribute this data correctly. .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 75500L, 37L, 130000L, 70L, 0L); + DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); assertEquals(9, delta.size()); // tunIface entries should not be changed. assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 50000L, 25L, 100000L, 50L, 0L); + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 500L, 2L, 200L, 5L, 0L); + DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 240000L, 100L, 120000L, 60L, 0L); + DEFAULT_NETWORK_NO, 240000L, 100L, 120000L, 60L, 0L); // Existing underlying Iface entries are updated assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 51000L, 35L, 102000L, 70L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L); // VPN underlying Iface entries are updated assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 25000L, 10L, 29800L, 15L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 25000L, 10L, 29800L, 15L, 0L); // New entries are added for new application's underlying Iface traffic assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, 500L, 2L, 200L, 5L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); // New entries are added for debug purpose assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, 50000L, 25L, 100000L, 50L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, 500, 2L, 200L, 5L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L); assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, - ROAMING_ALL, 50500L, 27L, 100200L, 55, 0); + ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0); } private static void assertContains(NetworkStats stats, String iface, int uid, int set, - int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes, - long txPackets, long operations) { - int index = stats.findIndex(iface, uid, set, tag, metered, roaming); + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + int index = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); assertTrue(index != -1); - assertValues(stats, index, iface, uid, set, tag, metered, roaming, + assertValues(stats, index, iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations); } private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set, - int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes, - long txPackets, long operations) { + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { final NetworkStats.Entry entry = stats.getValues(index, null); - assertValues(entry, iface, uid, set, tag, metered, roaming); + assertValues(entry, iface, uid, set, tag, metered, roaming, defaultNetwork); assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations); } private static void assertValues( NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered, - int roaming) { + int roaming, int defaultNetwork) { assertEquals(iface, entry.iface); assertEquals(uid, entry.uid); assertEquals(set, entry.set); assertEquals(tag, entry.tag); assertEquals(metered, entry.metered); assertEquals(roaming, entry.roaming); + assertEquals(defaultNetwork, entry.defaultNetwork); } private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets, diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java index fb2bd79fed3a..56b8e608dad1 100644 --- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java +++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java @@ -16,6 +16,7 @@ package com.android.internal.net; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_ALL; @@ -240,7 +241,8 @@ public class NetworkStatsFactoryTest { private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, int tag, long rxBytes, long txBytes) { - final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO); + final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO); if (i < 0) { fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", iface, uid, set, tag)); @@ -252,7 +254,8 @@ public class NetworkStatsFactoryTest { private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { - final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO); + final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO); if (i < 0) { fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", iface, uid, set, tag)); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b8e37f3a10ea..70cacb3af009 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -3501,34 +3501,50 @@ public class ConnectivityServiceTest { @Test public void testStatsIfacesChanged() throws Exception { mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + + Network[] onlyCell = new Network[]{mCellNetworkAgent.getNetwork()}; + Network[] onlyWifi = new Network[]{mWiFiNetworkAgent.getNetwork()}; // Simple connection should have updated ifaces mCellNetworkAgent.connect(false); waitForIdle(); - verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell); + reset(mStatsService); + + // Default network switch should update ifaces. + mWiFiNetworkAgent.connect(false); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyWifi); + reset(mStatsService); + + // Disconnect should update ifaces. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell); reset(mStatsService); // Metered change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell); reset(mStatsService); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell); reset(mStatsService); // Captive portal change shouldn't update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); waitForIdle(); - verify(mStatsService, never()).forceUpdateIfaces(); + verify(mStatsService, never()).forceUpdateIfaces(onlyCell); reset(mStatsService); // Roaming change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); waitForIdle(); - verify(mStatsService, atLeastOnce()).forceUpdateIfaces(); + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell); reset(mStatsService); } diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 1ddab5b47846..d9d4eeba900f 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -204,13 +204,13 @@ public class IpSecServiceParameterizedTest { } @Test - public void testCreateTransportModeTransform() throws Exception { + public void testCreateTransform() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); assertEquals(IpSecManager.Status.OK, createTransformResp.status); verify(mMockNetd) @@ -236,14 +236,14 @@ public class IpSecServiceParameterizedTest { } @Test - public void testCreateTransportModeTransformAead() throws Exception { + public void testCreateTransformAead() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); assertEquals(IpSecManager.Status.OK, createTransformResp.status); verify(mMockNetd) @@ -269,14 +269,14 @@ public class IpSecServiceParameterizedTest { } @Test - public void testDeleteTransportModeTransform() throws Exception { + public void testDeleteTransform() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); - mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId); + mIpSecService.createTransform(ipSecConfig, new Binder()); + mIpSecService.deleteTransform(createTransformResp.resourceId); verify(mMockNetd) .ipSecDeleteSecurityAssociation( @@ -302,7 +302,7 @@ public class IpSecServiceParameterizedTest { addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); @@ -334,7 +334,7 @@ public class IpSecServiceParameterizedTest { addAuthAndCryptToIpSecConfig(ipSecConfig); IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.createTransform(ipSecConfig, new Binder()); ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); int resourceId = createTransformResp.resourceId; @@ -353,7 +353,7 @@ public class IpSecServiceParameterizedTest { @Test public void testRemoveTransportModeTransform() throws Exception { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); - mIpSecService.removeTransportModeTransforms(pfd, 1); + mIpSecService.removeTransportModeTransforms(pfd); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); } diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index b2a27e8c27b0..659f9108c21a 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -412,9 +412,9 @@ public class IpSecServiceTest { } @Test - public void testDeleteInvalidTransportModeTransform() throws Exception { + public void testDeleteInvalidTransform() throws Exception { try { - mIpSecService.deleteTransportModeTransform(1); + mIpSecService.deleteTransform(1); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } @@ -423,7 +423,7 @@ public class IpSecServiceTest { @Test public void testRemoveTransportModeTransform() throws Exception { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); - mIpSecService.removeTransportModeTransforms(pfd, 1); + mIpSecService.removeTransportModeTransforms(pfd); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); } diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 10d6deba61df..9f2cb921ea8e 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -66,6 +66,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -174,6 +175,7 @@ public class IpConnectivityMetricsTest { } @Test + @Ignore public void testDefaultNetworkEvents() throws Exception { final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); @@ -292,6 +294,7 @@ public class IpConnectivityMetricsTest { } @Test + @Ignore public void testEndToEndLogging() throws Exception { // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. IpConnectivityLog logger = new IpConnectivityLog(mService.impl); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index c29363cd1a4f..1dbf9b2dfced 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -22,6 +22,7 @@ import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; 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; @@ -436,11 +437,13 @@ public class VpnTest { .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) .setLinkDownstreamBandwidthKbps(10)); networks.put(wifi, new NetworkCapabilities() .addTransportType(TRANSPORT_WIFI) .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_ROAMING) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) .setLinkUpstreamBandwidthKbps(20)); setMockedNetworks(networks); @@ -454,6 +457,7 @@ public class VpnTest { assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps); assertTrue(caps.hasTransport(TRANSPORT_VPN)); @@ -463,6 +467,7 @@ public class VpnTest { assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps); assertTrue(caps.hasTransport(TRANSPORT_VPN)); @@ -472,6 +477,7 @@ public class VpnTest { assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps); assertTrue(caps.hasTransport(TRANSPORT_VPN)); @@ -481,6 +487,7 @@ public class VpnTest { assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); } /** diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index dbaf8e690e07..6f1433286ca4 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -210,7 +210,7 @@ public class NetworkStatsCollectionTest { final NetworkStats.Entry entry = new NetworkStats.Entry(); final NetworkIdentitySet identSet = new NetworkIdentitySet(); identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - TEST_IMSI, null, false, true)); + TEST_IMSI, null, false, true, true)); int myUid = Process.myUid(); int otherUidInSameUser = Process.myUid() + 1; @@ -465,7 +465,7 @@ public class NetworkStatsCollectionTest { final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS); final NetworkIdentitySet ident = new NetworkIdentitySet(); ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null, - false, true)); + false, true, true)); large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B, new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0)); diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java index 2be5dae97a10..185c3ebfbcb9 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java @@ -18,6 +18,8 @@ package com.android.server.net; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.METERED_NO; import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_DEFAULT; @@ -224,6 +226,15 @@ public class NetworkStatsObserversTest { Mockito.verifyZeroInteractions(mockBinder); } + private NetworkIdentitySet makeTestIdentSet() { + NetworkIdentitySet identSet = new NetworkIdentitySet(); + identSet.add(new NetworkIdentity( + TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, + IMSI_1, null /* networkId */, false /* roaming */, true /* metered */, + true /* defaultNetwork */)); + return identSet; + } + @Test public void testUpdateStats_initialSample_doesNotNotify() throws Exception { DataUsageRequest inputRequest = new DataUsageRequest( @@ -235,10 +246,7 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveIfaces.put(TEST_IFACE, identSet); // Baseline @@ -263,10 +271,7 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveIfaces.put(TEST_IFACE, identSet); // Baseline @@ -298,10 +303,7 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveIfaces.put(TEST_IFACE, identSet); // Baseline @@ -334,17 +336,14 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveUidIfaces.put(TEST_IFACE, identSet); // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -352,7 +351,8 @@ public class NetworkStatsObserversTest { // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -371,17 +371,14 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveUidIfaces.put(TEST_IFACE, identSet); // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -389,7 +386,8 @@ public class NetworkStatsObserversTest { // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -407,17 +405,14 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveUidIfaces.put(TEST_IFACE, identSet); // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -425,7 +420,8 @@ public class NetworkStatsObserversTest { // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -444,17 +440,14 @@ public class NetworkStatsObserversTest { assertTrue(Objects.equals(sTemplateImsi1, request.template)); assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */)); + NetworkIdentitySet identSet = makeTestIdentSet(); mActiveUidIfaces.put(TEST_IFACE, identSet); // Baseline NetworkStats xtSnapshot = null; NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); @@ -462,8 +455,8 @@ public class NetworkStatsObserversTest { // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, - 2L, 0L); + ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, VPN_INFO, TEST_START); diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 65be5ffb32a2..0f26edb28009 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -21,6 +21,9 @@ import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.METERED_NO; @@ -67,6 +70,7 @@ import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsSession; import android.net.LinkProperties; +import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -136,6 +140,12 @@ public class NetworkStatsServiceTest { private static final int UID_BLUE = 1002; private static final int UID_GREEN = 1003; + + private static final Network WIFI_NETWORK = new Network(100); + private static final Network MOBILE_NETWORK = new Network(101); + private static final Network[] NETWORKS_WIFI = new Network[]{ WIFI_NETWORK }; + private static final Network[] NETWORKS_MOBILE = new Network[]{ MOBILE_NETWORK }; + private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs private static final int INVALID_TYPE = -1; @@ -230,7 +240,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -277,7 +287,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -305,10 +315,10 @@ public class NetworkStatsServiceTest { // verify service recorded history assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); - assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L, - 2L, 4); - assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L, - 256L, 2L, 6); + assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); + assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); @@ -330,10 +340,10 @@ public class NetworkStatsServiceTest { // after systemReady(), we should have historical stats loaded again assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); - assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, 512L, 4L, 256L, - 2L, 4); - assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, 512L, 4L, - 256L, 2L, 6); + assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); + assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); } @@ -355,7 +365,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // modify some number on wifi, and trigger poll event @@ -400,7 +410,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); // create some traffic on first network @@ -438,7 +448,7 @@ public class NetworkStatsServiceTest { .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); forcePollAndWaitForIdle(); @@ -480,7 +490,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // create some traffic @@ -542,7 +552,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); // create some traffic @@ -572,7 +582,7 @@ public class NetworkStatsServiceTest { .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); forcePollAndWaitForIdle(); @@ -604,7 +614,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // create some traffic for two apps @@ -640,12 +650,12 @@ public class NetworkStatsServiceTest { NetworkStats stats = mSession.getSummaryForAllUid( sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); assertEquals(3, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 50L, - 5L, 50L, 5L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 10L, - 1L, 10L, 1L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 50L, 5L, 50L, 5L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 10L, 1L, 10L, 1L, 1); assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 2048L, 16L, 1024L, 8L, 0); + DEFAULT_NETWORK_YES, 2048L, 16L, 1024L, 8L, 0); // now verify that recent history only contains one uid final long currentTime = currentTimeMillis(); @@ -653,7 +663,7 @@ public class NetworkStatsServiceTest { sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true); assertEquals(1, stats.size()); assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - 1024L, 8L, 512L, 4L, 0); + DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0); } @Test @@ -666,7 +676,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // create some initial traffic @@ -707,14 +717,14 @@ public class NetworkStatsServiceTest { final NetworkStats stats = mSession.getSummaryForAllUid( sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); assertEquals(4, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, - 2L, 128L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, - 1L, 64L, 1L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - 32L, 2L, 32L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, 1L, - 1L, 1L, 1L, 1); + DEFAULT_NETWORK_YES, 32L, 2L, 32L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 1L, 1L, 1L, 1); } @Test @@ -727,7 +737,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // create some initial traffic @@ -735,14 +745,14 @@ public class NetworkStatsServiceTest { expectCurrentTime(); expectDefaultSettings(); expectNetworkStatsSummary(buildEmptyStats()); - // Note that all traffic from NetworkManagementService is tagged as METERED_NO and - // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it - // on top by inspecting the iface properties. + // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO + // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer. + // We layer them on top by inspecting the iface properties. expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 128L, - 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, 64L, - 1L, 64L, 1L, 0L)); + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); mService.incrementOperationCount(UID_RED, 0xF00D, 1); forcePollAndWaitForIdle(); @@ -754,9 +764,9 @@ public class NetworkStatsServiceTest { sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); assertEquals(2, stats.size()); assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - 128L, 2L, 128L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, 64L, - 1L, 64L, 1L, 1); + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); } @Test @@ -769,7 +779,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); // Create some traffic @@ -782,9 +792,9 @@ public class NetworkStatsServiceTest { // on top by inspecting the iface properties. expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO, - 128L, 2L, 128L, 2L, 0L) - .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, 64L, - 1L, 64L, 1L, 0L)); + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) + .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); forcePollAndWaitForIdle(); // verify service recorded history @@ -795,9 +805,9 @@ public class NetworkStatsServiceTest { sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); assertEquals(2, stats.size()); assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES, - 128L, 2L, 128L, 2L, 0); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, 64L, - 1L, 64L, 1L, 0); + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0); } @Test @@ -810,7 +820,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_MOBILE); // create some tethering traffic @@ -855,7 +865,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(); + mService.forceUpdateIfaces(NETWORKS_WIFI); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -976,18 +986,18 @@ public class NetworkStatsServiceTest { // verify summary API final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end); assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - rxBytes, rxPackets, txBytes, txPackets, operations); + DEFAULT_NETWORK_ALL, rxBytes, rxPackets, txBytes, txPackets, operations); } private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) throws Exception { - assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, rxBytes, rxPackets, - txBytes, txPackets, operations); + assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + rxBytes, rxPackets, txBytes, txPackets, operations); } private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered, - int roaming, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) - throws Exception { + int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, + long txPackets, int operations) throws Exception { // verify history API final NetworkStatsHistory history = mSession.getHistoryForUid( template, uid, set, TAG_NONE, FIELD_ALL); @@ -997,8 +1007,8 @@ public class NetworkStatsServiceTest { // verify summary API final NetworkStats stats = mSession.getSummaryForAllUid( template, Long.MIN_VALUE, Long.MAX_VALUE, false); - assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, rxBytes, rxPackets, - txBytes, txPackets, operations); + assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, defaultNetwork, + rxBytes, rxPackets, txBytes, txPackets, operations); } private void expectSystemReady() throws Exception { @@ -1096,8 +1106,8 @@ public class NetworkStatsServiceTest { } private static void assertValues(NetworkStats stats, String iface, int uid, int set, - int tag, int metered, int roaming, long rxBytes, long rxPackets, long txBytes, - long txPackets, int operations) { + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, int operations) { final NetworkStats.Entry entry = new NetworkStats.Entry(); final int[] sets; if (set == SET_ALL) { @@ -1120,12 +1130,22 @@ public class NetworkStatsServiceTest { meterings = new int[] { metered }; } + final int[] defaultNetworks; + if (defaultNetwork == DEFAULT_NETWORK_ALL) { + defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, + DEFAULT_NETWORK_NO }; + } else { + defaultNetworks = new int[] { defaultNetwork }; + } + for (int s : sets) { for (int r : roamings) { for (int m : meterings) { - final int i = stats.findIndex(iface, uid, s, tag, m, r); - if (i != -1) { - entry.add(stats.getValues(i, null)); + for (int d : defaultNetworks) { + final int i = stats.findIndex(iface, uid, s, tag, m, r, d); + if (i != -1) { + entry.add(stats.getValues(i, null)); + } } } } @@ -1160,7 +1180,7 @@ public class NetworkStatsServiceTest { final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); - return new NetworkState(info, prop, capabilities, null, null, TEST_SSID); + return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); } private static NetworkState buildMobile3gState(String subscriberId) { @@ -1177,7 +1197,7 @@ public class NetworkStatsServiceTest { final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); - return new NetworkState(info, prop, capabilities, null, subscriberId, null); + return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null); } private static NetworkState buildMobile4gState(String iface) { @@ -1188,7 +1208,7 @@ public class NetworkStatsServiceTest { final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); - return new NetworkState(info, prop, capabilities, null, null, null); + return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null); } private NetworkStats buildEmptyStats() { diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java index 4098b989a16b..0504c79c55bd 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -62,6 +62,11 @@ public class ServiceManagerPermissionTests extends TestCase { public boolean isRuntimePermission(String permission) { return false; } + + @Override + public int getPackageUid(String packageName, int flags) { + return -1; + } }; ServiceManagerNative.asInterface(BinderInternal.getContextObject()) .setPermissionController(pc); diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 9905f827d663..95bf9210ba97 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -47,6 +47,13 @@ static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const Stri return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; } +template <typename T> +static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs, + const std::pair<StringPiece, Maybe<uint8_t>>& rhs) { + int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size()); + return name_cmp < 0 || (name_cmp == 0 && lhs->id < rhs.second); +} + ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, @@ -79,6 +86,22 @@ ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Mayb return package; } +ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name, + const Maybe<uint8_t> id) { + const auto last = packages.end(); + auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id), + less_than_struct_with_name_and_id<ResourceTablePackage>); + + if (iter != last && name == (*iter)->name && id == (*iter)->id) { + return iter->get(); + } + + std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>(); + new_package->name = name.to_string(); + new_package->id = id; + return packages.emplace(iter, std::move(new_package))->get(); +} + ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 95e30c442042..374fe1ea66a1 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -229,6 +229,11 @@ class ResourceTable { ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe<uint8_t> id = {}); + // Attempts to find a package having the specified name and ID. If not found, a new package + // of the specified parameters is created and returned. + ResourceTablePackage* CreatePackageAllowingDuplicateNames(const android::StringPiece& name, + const Maybe<uint8_t> id); + std::unique_ptr<ResourceTable> Clone() const; // The string pool used by this resource table. Values that reference strings must use @@ -239,7 +244,8 @@ class ResourceTable { // destroyed. StringPool string_pool; - // The list of packages in this table, sorted alphabetically by package name. + // The list of packages in this table, sorted alphabetically by package name and increasing + // package ID (missing ID being the lowest). std::vector<std::unique_ptr<ResourceTablePackage>> packages; // Set of dynamic packages that this table may reference. Their package names get encoded diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto index 8552195d338d..069360e3d6f3 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -306,10 +306,25 @@ message FileReference { } // A value that represents a primitive data type (float, int, boolean, etc.). -// Corresponds to the fields (type/data) of the C struct android::Res_value. message Primitive { - uint32 type = 1; - uint32 data = 2; + message NullType { + } + message EmptyType { + } + oneof oneof_value { + NullType null_value = 1; + EmptyType empty_value = 2; + float float_value = 3; + float dimension_value = 4; + float fraction_value = 5; + int32 int_decimal_value = 6; + uint32 int_hexidecimal_value = 7; + bool boolean_value = 8; + uint32 color_argb8_value = 9; + uint32 color_rgb8_value = 10; + uint32 color_argb4_value = 11; + uint32 color_rgb4_value = 12; + } } // A value that represents an XML attribute and what values it accepts. diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 964dacfeafef..d80307cd154a 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -47,7 +47,7 @@ class IApkSerializer { virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, IArchiveWriter* writer) = 0; virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0; - virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0; + virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0; virtual ~IApkSerializer() = default; @@ -65,19 +65,15 @@ bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer } if (apk->GetResourceTable() != nullptr) { - // Resource table - if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) { - context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) - << "failed to serialize the resource table"); - return false; - } + // The table might be modified by below code. + auto converted_table = apk->GetResourceTable(); // Resources - for (const auto& package : apk->GetResourceTable()->packages) { + for (const auto& package : converted_table->packages) { for (const auto& type : package->types) { for (const auto& entry : type->entries) { for (const auto& config_value : entry->values) { - const FileReference* file = ValueCast<FileReference>(config_value->value.get()); + FileReference* file = ValueCast<FileReference>(config_value->value.get()); if (file != nullptr) { if (file->file == nullptr) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) @@ -95,6 +91,13 @@ bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer } // entry } // type } // package + + // Converted resource table + if (!serializer->SerializeTable(converted_table, writer)) { + context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) + << "failed to serialize the resource table"); + return false; + } } // Other files @@ -158,7 +161,7 @@ class BinaryApkSerializer : public IApkSerializer { ArchiveEntry::kAlign, writer); } - bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override { + bool SerializeFile(FileReference* file, IArchiveWriter* writer) override { if (file->type == ResourceFile::Type::kProtoXml) { unique_ptr<io::InputStream> in = file->file->OpenInputStream(); if (in == nullptr) { @@ -189,6 +192,8 @@ class BinaryApkSerializer : public IApkSerializer { << "failed to serialize to binary XML: " << *file->path); return false; } + + file->type = ResourceFile::Type::kBinaryXml; } else { if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) { context_->GetDiagnostics()->Error(DiagMessage(source_) @@ -225,7 +230,7 @@ class ProtoApkSerializer : public IApkSerializer { ArchiveEntry::kCompress, writer); } - bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override { + bool SerializeFile(FileReference* file, IArchiveWriter* writer) override { if (file->type == ResourceFile::Type::kBinaryXml) { std::unique_ptr<io::IData> data = file->file->OpenAsData(); if (!data) { @@ -247,6 +252,8 @@ class ProtoApkSerializer : public IApkSerializer { << "failed to serialize to proto XML: " << *file->path); return false; } + + file->type = ResourceFile::Type::kProtoXml; } else { if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) { context_->GetDiagnostics()->Error(DiagMessage(source_) diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index d8635a989bed..81bc2c88939e 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -23,6 +23,7 @@ #include "Locale.h" #include "ResourceTable.h" #include "ResourceUtils.h" +#include "ResourceValues.h" #include "ValueVisitor.h" using ::android::ResStringPool; @@ -380,7 +381,8 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr std::map<ResourceId, ResourceNameRef> id_index; - ResourceTablePackage* pkg = out_table->CreatePackage(pb_package.package_name(), id); + ResourceTablePackage* pkg = + out_table->CreatePackageAllowingDuplicateNames(pb_package.package_name(), id); for (const pb::Type& pb_type : pb_package.type()) { const ResourceType* res_type = ParseResourceType(pb_type.name()); if (res_type == nullptr) { @@ -761,8 +763,66 @@ std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item, case pb::Item::kPrim: { const pb::Primitive& pb_prim = pb_item.prim(); - return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()), - pb_prim.data()); + android::Res_value val = {}; + switch (pb_prim.oneof_value_case()) { + case pb::Primitive::kNullValue: { + val.dataType = android::Res_value::TYPE_NULL; + val.data = android::Res_value::DATA_NULL_UNDEFINED; + } break; + case pb::Primitive::kEmptyValue: { + val.dataType = android::Res_value::TYPE_NULL; + val.data = android::Res_value::DATA_NULL_EMPTY; + } break; + case pb::Primitive::kFloatValue: { + val.dataType = android::Res_value::TYPE_FLOAT; + float float_val = pb_prim.float_value(); + val.data = *(uint32_t*)&float_val; + } break; + case pb::Primitive::kDimensionValue: { + val.dataType = android::Res_value::TYPE_DIMENSION; + float dimen_val = pb_prim.dimension_value(); + val.data = *(uint32_t*)&dimen_val; + } break; + case pb::Primitive::kFractionValue: { + val.dataType = android::Res_value::TYPE_FRACTION; + float fraction_val = pb_prim.fraction_value(); + val.data = *(uint32_t*)&fraction_val; + } break; + case pb::Primitive::kIntDecimalValue: { + val.dataType = android::Res_value::TYPE_INT_DEC; + val.data = static_cast<uint32_t>(pb_prim.int_decimal_value()); + } break; + case pb::Primitive::kIntHexidecimalValue: { + val.dataType = android::Res_value::TYPE_INT_HEX; + val.data = pb_prim.int_hexidecimal_value(); + } break; + case pb::Primitive::kBooleanValue: { + val.dataType = android::Res_value::TYPE_INT_BOOLEAN; + val.data = pb_prim.boolean_value() ? 0xFFFFFFFF : 0x0; + } break; + case pb::Primitive::kColorArgb8Value: { + val.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8; + val.data = pb_prim.color_argb8_value(); + } break; + case pb::Primitive::kColorRgb8Value: { + val.dataType = android::Res_value::TYPE_INT_COLOR_RGB8; + val.data = pb_prim.color_rgb8_value(); + } break; + case pb::Primitive::kColorArgb4Value: { + val.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4; + val.data = pb_prim.color_argb4_value(); + } break; + case pb::Primitive::kColorRgb4Value: { + val.dataType = android::Res_value::TYPE_INT_COLOR_RGB4; + val.data = pb_prim.color_rgb4_value(); + } break; + default: { + LOG(FATAL) << "Unexpected Primitive type: " + << static_cast<uint32_t>(pb_prim.oneof_value_case()); + return {}; + } break; + } + return util::make_unique<BinaryPrimitive>(val); } break; case pb::Item::kId: { diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index 78f12814389d..e9622f54f6ae 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -436,8 +436,51 @@ class ValueSerializer : public ConstValueVisitor { prim->Flatten(&val); pb::Primitive* pb_prim = out_value_->mutable_item()->mutable_prim(); - pb_prim->set_type(val.dataType); - pb_prim->set_data(val.data); + + switch (val.dataType) { + case android::Res_value::TYPE_NULL: { + if (val.data == android::Res_value::DATA_NULL_UNDEFINED) { + pb_prim->set_allocated_null_value(new pb::Primitive_NullType()); + } else if (val.data == android::Res_value::DATA_NULL_EMPTY) { + pb_prim->set_allocated_empty_value(new pb::Primitive_EmptyType()); + } else { + LOG(FATAL) << "Unexpected data value for TYPE_NULL BinaryPrimitive: " << val.data; + } + } break; + case android::Res_value::TYPE_FLOAT: { + pb_prim->set_float_value(*(float*)&val.data); + } break; + case android::Res_value::TYPE_DIMENSION: { + pb_prim->set_dimension_value(*(float*)&val.data); + } break; + case android::Res_value::TYPE_FRACTION: { + pb_prim->set_fraction_value(*(float*)&val.data); + } break; + case android::Res_value::TYPE_INT_DEC: { + pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data)); + } break; + case android::Res_value::TYPE_INT_HEX: { + pb_prim->set_int_hexidecimal_value(val.data); + } break; + case android::Res_value::TYPE_INT_BOOLEAN: { + pb_prim->set_boolean_value(static_cast<bool>(val.data)); + } break; + case android::Res_value::TYPE_INT_COLOR_ARGB8: { + pb_prim->set_color_argb8_value(val.data); + } break; + case android::Res_value::TYPE_INT_COLOR_RGB8: { + pb_prim->set_color_rgb8_value(val.data); + } break; + case android::Res_value::TYPE_INT_COLOR_ARGB4: { + pb_prim->set_color_argb4_value(val.data); + } break; + case android::Res_value::TYPE_INT_COLOR_RGB4: { + pb_prim->set_color_rgb4_value(val.data); + } break; + default: + LOG(FATAL) << "Unexpected BinaryPrimitive type: " << val.dataType; + break; + } } void Visit(const Attribute* attr) override { diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index ccba5c652822..9081ab6abd0a 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -254,6 +254,111 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeXml) { EXPECT_THAT(child_text->text, StrEq("woah there")); } +TEST(ProtoSerializeTest, SerializeAndDeserializePrimitives) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddValue("android:bool/boolean_true", + test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, true)) + .AddValue("android:bool/boolean_false", + test::BuildPrimitive(android::Res_value::TYPE_INT_BOOLEAN, false)) + .AddValue("android:color/color_rgb8", ResourceUtils::TryParseColor("#AABBCC")) + .AddValue("android:color/color_argb8", ResourceUtils::TryParseColor("#11223344")) + .AddValue("android:color/color_rgb4", ResourceUtils::TryParseColor("#DEF")) + .AddValue("android:color/color_argb4", ResourceUtils::TryParseColor("#5678")) + .AddValue("android:integer/integer_444", ResourceUtils::TryParseInt("444")) + .AddValue("android:integer/integer_neg_333", ResourceUtils::TryParseInt("-333")) + .AddValue("android:integer/hex_int_abcd", ResourceUtils::TryParseInt("0xABCD")) + .AddValue("android:dimen/dimen_1.39mm", ResourceUtils::TryParseFloat("1.39mm")) + .AddValue("android:fraction/fraction_27", ResourceUtils::TryParseFloat("27%")) + .AddValue("android:integer/null", ResourceUtils::MakeEmpty()) + .Build(); + + pb::ResourceTable pb_table; + SerializeTableToPb(*table, &pb_table); + + test::TestFile file_a("res/layout/main.xml"); + MockFileCollection files; + EXPECT_CALL(files, FindFile(Eq("res/layout/main.xml"))) + .WillRepeatedly(::testing::Return(&file_a)); + + ResourceTable new_table; + std::string error; + ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)); + EXPECT_THAT(error, IsEmpty()); + + BinaryPrimitive* bp = test::GetValueForConfigAndProduct<BinaryPrimitive>( + &new_table, "android:bool/boolean_true", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseBool("true")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:bool/boolean_false", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseBool("false")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:color/color_rgb8", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_COLOR_RGB8)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseColor("#AABBCC")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:color/color_argb8", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_COLOR_ARGB8)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseColor("#11223344")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:color/color_rgb4", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_COLOR_RGB4)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseColor("#DEF")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:color/color_argb4", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_COLOR_ARGB4)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseColor("#5678")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:integer/integer_444", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("444")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>( + &new_table, "android:integer/integer_neg_333", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("-333")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>( + &new_table, "android:integer/hex_int_abcd", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_HEX)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("0xABCD")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:dimen/dimen_1.39mm", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_DIMENSION)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("1.39mm")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>( + &new_table, "android:fraction/fraction_27", ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_FRACTION)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("27%")->value.data)); + + bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:integer/null", + ConfigDescription::DefaultConfig(), ""); + ASSERT_THAT(bp, NotNull()); + EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_NULL)); + EXPECT_THAT(bp->value.data, Eq(ResourceUtils::MakeEmpty()->value.data)); +} + static void ExpectConfigSerializes(const StringPiece& config_str) { const ConfigDescription expected_config = test::ParseConfigOrDie(config_str); pb::Configuration pb_config; diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index ccc3470fd7f4..713db5b7ed86 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -319,6 +319,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["original-package"]; manifest_action["overlay"]; manifest_action["protected-broadcast"]; + manifest_action["adopt-permissions"]; manifest_action["uses-permission"]; manifest_action["uses-permission-sdk-23"]; manifest_action["permission"]; diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index 15d39fdfd6ea..ec40a2229c33 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -270,6 +270,9 @@ def parse_fonts_xml(fonts_xml_path): if index: index = int(index) + if not path.exists(path.join(_fonts_dir, font_file)): + continue # Missing font is a valid case. Just ignore the missing font files. + record = FontRecord( name, frozenset(scripts), diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl new file mode 100644 index 000000000000..b8d2971e74bb --- /dev/null +++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl @@ -0,0 +1,44 @@ +/* + * 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.net.wifi; + +/** + * Interface for Soft AP callback. + * + * @hide + */ +oneway interface ISoftApCallback +{ + /** + * Service to manager callback providing current soft AP state. The possible + * parameter values listed are defined in WifiManager.java + * + * @param state new AP state. One of WIFI_AP_STATE_DISABLED, + * WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, + * WIFI_AP_STATE_ENABLING, WIFI_AP_STATE_FAILED + * @param failureReason reason when in failed state. One of + * SAP_START_FAILURE_GENERAL, SAP_START_FAILURE_NO_CHANNEL + */ + void onStateChanged(int state, int failureReason); + + /** + * Service to manager callback providing number of connected clients. + * + * @param numClients number of connected clients + */ + void onNumClientsChanged(int numClients); +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 70d6ce46bcf0..e9e61a546e14 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -23,15 +23,15 @@ import android.net.wifi.hotspot2.OsuProvider; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.IProvisioningCallback; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.ScanSettings; -import android.net.wifi.ScanResult; +import android.net.DhcpInfo; +import android.net.Network; +import android.net.wifi.ISoftApCallback; import android.net.wifi.PasspointManagementObjectDefinition; +import android.net.wifi.ScanResult; +import android.net.wifi.ScanSettings; import android.net.wifi.WifiActivityEnergyInfo; -import android.net.Network; - -import android.net.DhcpInfo; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; import android.os.Messenger; import android.os.ResultReceiver; @@ -178,5 +178,9 @@ interface IWifiManager void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData); void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback); + + void registerSoftApCallback(in IBinder binder, in ISoftApCallback callback, int callbackIdentifier); + + void unregisterSoftApCallback(int callbackIdentifier); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 6438631cb8ed..2c6011823828 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -267,8 +267,15 @@ public class WifiConfiguration implements Parcelable { public static final int AP_BAND_5GHZ = 1; /** + * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability, + * operating country code and current radio conditions. + * @hide + */ + public static final int AP_BAND_ANY = -1; + + /** * The band which AP resides on - * 0-2G 1-5G + * -1:Any 0:2G 1:5G * By default, 2G is chosen * @hide */ diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index aa75a0703a84..99080d6cc2c0 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -55,6 +56,8 @@ import com.android.server.net.NetworkPinner; import dalvik.system.CloseGuard; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.net.InetAddress; import java.util.Collections; @@ -432,6 +435,17 @@ public class WifiManager { */ public static final String EXTRA_WIFI_AP_MODE = "wifi_ap_mode"; + /** @hide */ + @IntDef(flag = false, prefix = { "WIFI_AP_STATE_" }, value = { + WIFI_AP_STATE_DISABLING, + WIFI_AP_STATE_DISABLED, + WIFI_AP_STATE_ENABLING, + WIFI_AP_STATE_ENABLED, + WIFI_AP_STATE_FAILED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiApState {} + /** * Wi-Fi AP is currently being disabled. The state will change to * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully. @@ -486,6 +500,14 @@ public class WifiManager { @SystemApi public static final int WIFI_AP_STATE_FAILED = 14; + /** @hide */ + @IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = { + SAP_START_FAILURE_GENERAL, + SAP_START_FAILURE_NO_CHANNEL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SapStartFailure {} + /** * If WIFI AP start failed, this reason code means there is no legal channel exists on * user selected band by regulatory @@ -2324,6 +2346,119 @@ public class WifiManager { } /** + * Base class for soft AP callback. Should be extended by applications and set when calling + * {@link WifiManager#registerSoftApCallback(SoftApCallback, Handler)}. + * + * @hide + */ + public interface SoftApCallback { + /** + * Called when soft AP state changes. + * + * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED}, + * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED}, + * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED} + * @param failureReason reason when in failed state. One of + * {@link #SAP_START_FAILURE_GENERAL}, {@link #SAP_START_FAILURE_NO_CHANNEL} + */ + public abstract void onStateChanged(@WifiApState int state, + @SapStartFailure int failureReason); + + /** + * Called when number of connected clients to soft AP changes. + * + * @param numClients number of connected clients + */ + public abstract void onNumClientsChanged(int numClients); + } + + /** + * Callback proxy for SoftApCallback objects. + * + * @hide + */ + private static class SoftApCallbackProxy extends ISoftApCallback.Stub { + private final Handler mHandler; + private final SoftApCallback mCallback; + + SoftApCallbackProxy(Looper looper, SoftApCallback callback) { + mHandler = new Handler(looper); + mCallback = callback; + } + + @Override + public void onStateChanged(int state, int failureReason) throws RemoteException { + Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state + ", failureReason=" + + failureReason); + mHandler.post(() -> { + mCallback.onStateChanged(state, failureReason); + }); + } + + @Override + public void onNumClientsChanged(int numClients) throws RemoteException { + Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients); + mHandler.post(() -> { + mCallback.onNumClientsChanged(numClients); + }); + } + } + + /** + * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current + * soft AP state and number of connected devices immediately after a successful call to this API + * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state + * indicates that the latest attempt to start soft AP has failed. Caller can unregister a + * previously registered callback using {@link unregisterSoftApCallback} + * <p> + * Applications should have the + * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers + * without the permission will trigger a {@link java.lang.SecurityException}. + * <p> + * + * @param callback Callback for soft AP events + * @param handler The Handler on whose thread to execute the callbacks of the {@code callback} + * object. If null, then the application's main thread will be used. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void registerSoftApCallback(@NonNull SoftApCallback callback, + @Nullable Handler handler) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", handler=" + handler); + + Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); + Binder binder = new Binder(); + try { + mService.registerSoftApCallback(binder, new SoftApCallbackProxy(looper, callback), + callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allow callers to unregister a previously registered callback. After calling this method, + * applications will no longer receive soft AP events. + * + * @param callback Callback to unregister for soft AP events + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void unregisterSoftApCallback(@NonNull SoftApCallback callback) { + if (callback == null) throw new IllegalArgumentException("callback cannot be null"); + Log.v(TAG, "unregisterSoftApCallback: callback=" + callback); + + try { + mService.unregisterSoftApCallback(callback.hashCode()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * LocalOnlyHotspotReservation that contains the {@link WifiConfiguration} for the active * LocalOnlyHotspot request. * <p> diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 0df5615385fe..4b5f6452d392 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -24,11 +24,19 @@ import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMP import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED; +import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; +import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.*; import android.content.Context; @@ -37,6 +45,7 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription; +import android.net.wifi.WifiManager.SoftApCallback; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -66,6 +75,7 @@ public class WifiManagerTest { @Mock ApplicationInfo mApplicationInfo; @Mock WifiConfiguration mApConfig; @Mock IBinder mAppBinder; + @Mock SoftApCallback mSoftApCallback; private Handler mHandler; private TestLooper mLooper; @@ -632,6 +642,149 @@ public class WifiManagerTest { } /** + * Verify an IllegalArgumentException is thrown if callback is not provided. + */ + @Test + public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() { + try { + mWifiManager.registerSoftApCallback(null, mHandler); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + + /** + * Verify an IllegalArgumentException is thrown if callback is not provided. + */ + @Test + public void unregisterSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() { + try { + mWifiManager.unregisterSoftApCallback(null); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + + /** + * Verify main looper is used when handler is not provided. + */ + @Test + public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() { + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + mWifiManager.registerSoftApCallback(mSoftApCallback, null); + verify(mContext).getMainLooper(); + } + + /** + * Verify the call to registerSoftApCallback goes to WifiServiceImpl. + */ + @Test + public void registerSoftApCallbackCallGoesToWifiServiceImpl() throws Exception { + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), + any(ISoftApCallback.Stub.class), anyInt()); + } + + /** + * Verify the call to unregisterSoftApCallback goes to WifiServiceImpl. + */ + @Test + public void unregisterSoftApCallbackCallGoesToWifiServiceImpl() throws Exception { + ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class); + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), + any(ISoftApCallback.Stub.class), callbackIdentifier.capture()); + + mWifiManager.unregisterSoftApCallback(mSoftApCallback); + verify(mWifiService).unregisterSoftApCallback(eq((int) callbackIdentifier.getValue())); + } + + /* + * Verify client provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnStateChanged() throws Exception { + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLED, 0); + mLooper.dispatchAll(); + verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0); + } + + /* + * Verify client provided callback is being called through callback proxy + */ + @Test + public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception { + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + final int testNumClients = 3; + callbackCaptor.getValue().onNumClientsChanged(testNumClients); + mLooper.dispatchAll(); + verify(mSoftApCallback).onNumClientsChanged(testNumClients); + } + + /* + * Verify client provided callback is being called through callback proxy on multiple events + */ + @Test + public void softApCallbackProxyCallsOnMultipleUpdates() throws Exception { + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + final int testNumClients = 5; + callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLING, 0); + callbackCaptor.getValue().onNumClientsChanged(testNumClients); + callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL); + + mLooper.dispatchAll(); + verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0); + verify(mSoftApCallback).onNumClientsChanged(testNumClients); + verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL); + } + + /* + * Verify client provided callback is being called on the correct thread + */ + @Test + public void softApCallbackIsCalledOnCorrectThread() throws Exception { + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + TestLooper altLooper = new TestLooper(); + Handler altHandler = new Handler(altLooper.getLooper()); + mWifiManager.registerSoftApCallback(mSoftApCallback, altHandler); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(), + anyInt()); + + callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLED, 0); + altLooper.dispatchAll(); + verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0); + } + + /** + * Verify that the handler provided by the caller is used for registering soft AP callback. + */ + @Test + public void testCorrectLooperIsUsedForSoftApCallbackHandler() throws Exception { + mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler); + mLooper.dispatchAll(); + verify(mWifiService).registerSoftApCallback(any(IBinder.class), + any(ISoftApCallback.Stub.class), anyInt()); + verify(mContext, never()).getMainLooper(); + } + + /** * Verify that the handler provided by the caller is used for the observer. */ @Test |